diff --git a/assets/electrum_server_list.yml b/assets/electrum_server_list.yml index 661cadc9c..2b6649271 100644 --- a/assets/electrum_server_list.yml +++ b/assets/electrum_server_list.yml @@ -1,2 +1,2 @@ - - uri: electrumx.cakewallet.com:50002 \ No newline at end of file + uri: electrum.cakewallet.com:50002 \ No newline at end of file diff --git a/assets/images/2.0x/transfer.png b/assets/images/2.0x/transfer.png new file mode 100644 index 000000000..61be79142 Binary files /dev/null and b/assets/images/2.0x/transfer.png differ diff --git a/assets/images/2.0x/upload.png b/assets/images/2.0x/upload.png new file mode 100644 index 000000000..bb6dfbf44 Binary files /dev/null and b/assets/images/2.0x/upload.png differ diff --git a/assets/images/3.0x/transfer.png b/assets/images/3.0x/transfer.png new file mode 100644 index 000000000..049253da5 Binary files /dev/null and b/assets/images/3.0x/transfer.png differ diff --git a/assets/images/3.0x/upload.png b/assets/images/3.0x/upload.png new file mode 100644 index 000000000..f42a50eca Binary files /dev/null and b/assets/images/3.0x/upload.png differ diff --git a/assets/images/transfer.png b/assets/images/transfer.png index 383c35fb1..d388f818f 100644 Binary files a/assets/images/transfer.png and b/assets/images/transfer.png differ diff --git a/assets/images/upload.png b/assets/images/upload.png index dba320367..fefd5f660 100644 Binary files a/assets/images/upload.png and b/assets/images/upload.png differ diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp index 2dfd8a0ae..efe8c49f1 100644 --- a/cw_monero/ios/Classes/monero_api.cpp +++ b/cw_monero/ios/Classes/monero_api.cpp @@ -294,14 +294,26 @@ extern "C" return true; } - void load_wallet(char *path, char *password, int32_t nettype) + bool load_wallet(char *path, char *password, int32_t nettype) { nice(19); Monero::NetworkType networkType = static_cast(nettype); - Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->openWallet(std::string(path), std::string(password), networkType); + Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); + Monero::Wallet *wallet = walletManager->openWallet(std::string(path), std::string(password), networkType); + int status; + std::string errorString; + + wallet->statusWithErrorString(status, errorString); change_current_wallet(wallet); + + return !(status != Monero::Wallet::Status_Ok || !errorString.empty()); } + char *error_string() { + return strdup(get_current_wallet()->errorString().c_str()); + } + + bool is_wallet_exist(char *path) { return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path)); diff --git a/cw_monero/lib/exceptions/wallet_opening_exception.dart b/cw_monero/lib/exceptions/wallet_opening_exception.dart new file mode 100644 index 000000000..8d84b0f7e --- /dev/null +++ b/cw_monero/lib/exceptions/wallet_opening_exception.dart @@ -0,0 +1,8 @@ +class WalletOpeningException implements Exception { + WalletOpeningException({this.message}); + + final String message; + + @override + String toString() => message; +} \ No newline at end of file diff --git a/cw_monero/lib/signatures.dart b/cw_monero/lib/signatures.dart index 5e9c4fa9d..0f75288e7 100644 --- a/cw_monero/lib/signatures.dart +++ b/cw_monero/lib/signatures.dart @@ -14,7 +14,9 @@ typedef restore_wallet_from_keys = Int8 Function(Pointer, Pointer, typedef is_wallet_exist = Int8 Function(Pointer); -typedef load_wallet = Void Function(Pointer, Pointer, Int8); +typedef load_wallet = Int8 Function(Pointer, Pointer, Int8); + +typedef error_string = Pointer Function(); typedef get_filename = Pointer Function(); diff --git a/cw_monero/lib/types.dart b/cw_monero/lib/types.dart index 602a33572..1cc1a6055 100644 --- a/cw_monero/lib/types.dart +++ b/cw_monero/lib/types.dart @@ -14,7 +14,9 @@ typedef RestoreWalletFromKeys = int Function(Pointer, Pointer, typedef IsWalletExist = int Function(Pointer); -typedef LoadWallet = void Function(Pointer, Pointer, int); +typedef LoadWallet = int Function(Pointer, Pointer, int); + +typedef ErrorString = Pointer Function(); typedef GetFilename = Pointer Function(); diff --git a/cw_monero/lib/wallet_manager.dart b/cw_monero/lib/wallet_manager.dart index 07f534c97..e48055cf9 100644 --- a/cw_monero/lib/wallet_manager.dart +++ b/cw_monero/lib/wallet_manager.dart @@ -1,4 +1,5 @@ import 'dart:ffi'; +import 'package:cw_monero/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/wallet.dart'; import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; @@ -32,6 +33,10 @@ final loadWalletNative = moneroApi .lookup>('load_wallet') .asFunction(); +final errorStringNative = moneroApi + .lookup>('error_string') + .asFunction(); + void createWalletSync( {String path, String password, String language, int nettype = 0}) { final pathPointer = Utf8.toUtf8(path); @@ -136,10 +141,14 @@ void restoreWalletFromKeysSync( void loadWallet({String path, String password, int nettype = 0}) { final pathPointer = Utf8.toUtf8(path); final passwordPointer = Utf8.toUtf8(password); - - loadWalletNative(pathPointer, passwordPointer, nettype); + final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0; free(pathPointer); free(passwordPointer); + + if (!loaded) { + throw WalletOpeningException( + message: convertUTF8ToString(pointer: errorStringNative())); + } } void _createWallet(Map args) { diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ca92f3f09..69d04462b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 17; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -371,7 +371,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.9; + MARKETING_VERSION = 4.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -494,7 +494,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 17; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -511,7 +511,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.9; + MARKETING_VERSION = 4.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -528,7 +528,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 17; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -545,7 +545,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.9; + MARKETING_VERSION = 4.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/bitcoin/address_to_output_script.dart b/lib/bitcoin/address_to_output_script.dart new file mode 100644 index 000000000..2f6698ff0 --- /dev/null +++ b/lib/bitcoin/address_to_output_script.dart @@ -0,0 +1,25 @@ +import 'dart:typed_data'; +import 'package:bs58check/bs58check.dart' as bs58check; +import 'package:bitcoin_flutter/src/utils/constants/op.dart'; +import 'package:bitcoin_flutter/src/utils/script.dart' as bscript; +import 'package:bitcoin_flutter/src/address.dart'; + + +Uint8List p2shAddressToOutputScript(String address) { + final decodeBase58 = bs58check.decode(address); + final hash = decodeBase58.sublist(1); + return bscript.compile([OPS['OP_HASH160'], hash, OPS['OP_EQUAL']]); +} + +Uint8List addressToOutputScript(String address) { + try { + // FIXME: improve validation for p2sh addresses + if (address.startsWith('3')) { + return p2shAddressToOutputScript(address); + } + + return Address.addressToOutputScript(address); + } catch (_) { + return Uint8List(0); + } +} \ No newline at end of file diff --git a/lib/bitcoin/bitcoin_address_record.dart b/lib/bitcoin/bitcoin_address_record.dart index 98cd1c9da..af492de2d 100644 --- a/lib/bitcoin/bitcoin_address_record.dart +++ b/lib/bitcoin/bitcoin_address_record.dart @@ -1,19 +1,25 @@ import 'dart:convert'; +import 'package:quiver/core.dart'; class BitcoinAddressRecord { - BitcoinAddressRecord(this.address, {this.label, this.index}); + BitcoinAddressRecord(this.address, {this.index}); factory BitcoinAddressRecord.fromJSON(String jsonSource) { final decoded = json.decode(jsonSource) as Map; return BitcoinAddressRecord(decoded['address'] as String, - label: decoded['label'] as String, index: decoded['index'] as int); + index: decoded['index'] as int); } + @override + bool operator ==(Object o) => + o is BitcoinAddressRecord && address == o.address; + final String address; int index; - String label; - String toJSON() => - json.encode({'label': label, 'address': address, 'index': index}); + @override + int get hashCode => address.hashCode; + + String toJSON() => json.encode({'address': address, 'index': index}); } diff --git a/lib/bitcoin/bitcoin_amount_format.dart b/lib/bitcoin/bitcoin_amount_format.dart index 75db6c314..fa00387f1 100644 --- a/lib/bitcoin/bitcoin_amount_format.dart +++ b/lib/bitcoin/bitcoin_amount_format.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:intl/intl.dart'; import 'package:cake_wallet/entities/crypto_amount_format.dart'; @@ -7,10 +9,32 @@ final bitcoinAmountFormat = NumberFormat() ..maximumFractionDigits = bitcoinAmountLength ..minimumFractionDigits = 1; -String bitcoinAmountToString({int amount}) => - bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); +String bitcoinAmountToString({int amount}) => bitcoinAmountFormat.format( + cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); -double bitcoinAmountToDouble({int amount}) => cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); +double bitcoinAmountToDouble({int amount}) => + cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); -int doubleToBitcoinAmount(double amount) => - (amount * bitcoinAmountDivider).toInt(); \ No newline at end of file +int stringDoubleToBitcoinAmount(String amount) { + final splitted = amount.split(''); + final dotIndex = amount.indexOf('.'); + int result = 0; + + + for (var i = 0; i < splitted.length; i++) { + try { + if (dotIndex == i) { + continue; + } + + final char = splitted[i]; + final multiplier = dotIndex < i + ? bitcoinAmountDivider ~/ pow(10, (i - dotIndex)) + : (bitcoinAmountDivider * pow(10, (dotIndex - i -1))).toInt(); + final num = int.parse(char) * multiplier; + result += num; + } catch (_) {} + } + + return result; +} diff --git a/lib/bitcoin/bitcoin_balance.dart b/lib/bitcoin/bitcoin_balance.dart index 6aefe28f4..7d8441250 100644 --- a/lib/bitcoin/bitcoin_balance.dart +++ b/lib/bitcoin/bitcoin_balance.dart @@ -5,7 +5,8 @@ import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/entities/balance.dart'; class BitcoinBalance extends Balance { - const BitcoinBalance({@required this.confirmed, @required this.unconfirmed}) : super(); + const BitcoinBalance({@required this.confirmed, @required this.unconfirmed}) + : super(confirmed, unconfirmed); factory BitcoinBalance.fromJSON(String jsonSource) { if (jsonSource == null) { @@ -22,13 +23,12 @@ class BitcoinBalance extends Balance { final int confirmed; final int unconfirmed; - int get total => confirmed + unconfirmed; + @override + String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed); - String get confirmedFormatted => bitcoinAmountToString(amount: confirmed); - - String get unconfirmedFormatted => bitcoinAmountToString(amount: unconfirmed); - - String get totalFormatted => bitcoinAmountToString(amount: total); + @override + String get formattedAdditionalBalance => + bitcoinAmountToString(amount: unconfirmed); String toJSON() => json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed}); diff --git a/lib/bitcoin/bitcoin_mnemonic.dart b/lib/bitcoin/bitcoin_mnemonic.dart new file mode 100644 index 000000000..7f4bcd13c --- /dev/null +++ b/lib/bitcoin/bitcoin_mnemonic.dart @@ -0,0 +1,2308 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; +import 'package:crypto/crypto.dart'; +import 'package:unorm_dart/unorm_dart.dart' as unorm; +import 'package:cryptography/cryptography.dart' as cryptography; + +const segwit = '100'; +final wordlist = englishWordlist; + +Uint8List randomBytes(int length, {bool secure = false}) { + assert(length > 0); + + final random = secure ? Random.secure() : Random(); + final ret = Uint8List(length); + + for (var i = 0; i < length; i++) { + ret[i] = random.nextInt(256); + } + + return ret; +} + +double logBase(num x, num base) => log(x) / log(base); + +String mnemonicEncode(int i) { + var _i = i; + final n = wordlist.length; + final words = []; + + while (_i > 0) { + final x = i % n; + _i = (i / n).floor(); + words.add(wordlist[x]); + } + + return words.join(' '); +} + +int mnemonicDecode(String seed) { + var i = 0; + final n = wordlist.length; + final words = seed.split(' '); + + while (words.length > 0) { + final word = words.removeLast(); + final k = wordlist.indexOf(word); + i = i * n + k; + } + + return i; +} + +bool isNewSeed(String seed, {String prefix = segwit}) { + final hmacSha512 = Hmac(sha512, utf8.encode('Seed version')); + final digest = hmacSha512.convert(utf8.encode(normalizeText(seed))); + final hx = digest.toString(); + return hx.startsWith(prefix.toLowerCase()); +} + +void maskBytes(Uint8List bytes, int bits) { + final skipCount = (bits / 8).floor(); + var lastByte = (1 << bits % 8) - 1; + + for (var i = bytes.length - 1 - skipCount; i >= 0; i--) { + bytes[i] &= lastByte; + + if (lastByte > 0) { + lastByte = 0; + } + } +} + +String bufferToBin(Uint8List data) { + final q1 = data.map((e) => e.toRadixString(2).padLeft(8, '0')); + final q2 = q1.join(''); + return q2; +} + +String encode(Uint8List data) { + final dataBitLen = data.length * 8; + final wordBitLen = logBase(wordlist.length, 2).ceil(); + final wordCount = (dataBitLen / wordBitLen).floor(); + maskBytes(data, wordCount * wordBitLen); + final bin = bufferToBin(data); + final binStr = bin.substring(bin.length - (wordCount * wordBitLen)); + final result = []; + + for (var i = 0; i < wordCount; i++) { + final wordBin = binStr.substring(i * wordBitLen, (i + 1) * wordBitLen); + result.add(wordlist[int.parse(wordBin, radix: 2)]); + } + + return result.join(' '); +} + +List prefixMatches(String source, List prefixes) { + final hmacSha512 = Hmac(sha512, utf8.encode('Seed version')); + final digest = hmacSha512.convert(utf8.encode(normalizeText(source))); + final hx = digest.toString(); + + return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList(); +} + +String generateMnemonic({int strength = 132, String prefix = segwit}) { + final wordBitlen = logBase(wordlist.length, 2).ceil(); + final wordCount = strength / wordBitlen; + final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil(); + var result = ''; + + do { + final bytes = randomBytes(byteCount); + maskBytes(bytes, strength); + result = encode(bytes); + } while (!prefixMatches(result, [prefix]).first); + + return result; +} + +Uint8List mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) { + final pbkdf2 = cryptography.Pbkdf2( + macAlgorithm: cryptography.Hmac(cryptography.sha512), + iterations: 2048, + bits: 512); + final text = normalizeText(mnemonic); + + return pbkdf2.deriveBitsSync(text.codeUnits, + nonce: cryptography.Nonce('electrum'.codeUnits)); +} + +bool matchesAnyPrefix(String mnemonic) => + prefixMatches(mnemonic, [segwit]).any((el) => el); + +bool validateMnemonic(String mnemonic, {String prefix = segwit}) { + try { + return matchesAnyPrefix(mnemonic); + } catch(e) { + return false; + } +} + +final COMBININGCODEPOINTS = combiningcodepoints(); + +List combiningcodepoints() { + final source = '300:34e|350:36f|483:487|591:5bd|5bf|5c1|5c2|5c4|5c5|5c7|610:61a|64b:65f|670|' + + '6d6:6dc|6df:6e4|6e7|6e8|6ea:6ed|711|730:74a|7eb:7f3|816:819|81b:823|825:827|' + + '829:82d|859:85b|8d4:8e1|8e3:8ff|93c|94d|951:954|9bc|9cd|a3c|a4d|abc|acd|b3c|' + + 'b4d|bcd|c4d|c55|c56|cbc|ccd|d4d|dca|e38:e3a|e48:e4b|eb8|eb9|ec8:ecb|f18|f19|' + + 'f35|f37|f39|f71|f72|f74|f7a:f7d|f80|f82:f84|f86|f87|fc6|1037|1039|103a|108d|' + + '135d:135f|1714|1734|17d2|17dd|18a9|1939:193b|1a17|1a18|1a60|1a75:1a7c|1a7f|' + + '1ab0:1abd|1b34|1b44|1b6b:1b73|1baa|1bab|1be6|1bf2|1bf3|1c37|1cd0:1cd2|' + + '1cd4:1ce0|1ce2:1ce8|1ced|1cf4|1cf8|1cf9|1dc0:1df5|1dfb:1dff|20d0:20dc|20e1|' + + '20e5:20f0|2cef:2cf1|2d7f|2de0:2dff|302a:302f|3099|309a|a66f|a674:a67d|a69e|' + + 'a69f|a6f0|a6f1|a806|a8c4|a8e0:a8f1|a92b:a92d|a953|a9b3|a9c0|aab0|aab2:aab4|' + + 'aab7|aab8|aabe|aabf|aac1|aaf6|abed|fb1e|fe20:fe2f|101fd|102e0|10376:1037a|' + + '10a0d|10a0f|10a38:10a3a|10a3f|10ae5|10ae6|11046|1107f|110b9|110ba|11100:11102|' + + '11133|11134|11173|111c0|111ca|11235|11236|112e9|112ea|1133c|1134d|11366:1136c|' + + '11370:11374|11442|11446|114c2|114c3|115bf|115c0|1163f|116b6|116b7|1172b|11c3f|' + + '16af0:16af4|16b30:16b36|1bc9e|1d165:1d169|1d16d:1d172|1d17b:1d182|1d185:1d18b|' + + '1d1aa:1d1ad|1d242:1d244|1e000:1e006|1e008:1e018|1e01b:1e021|1e023|1e024|' + + '1e026:1e02a|1e8d0:1e8d6|1e944:1e94a'; + + return source.split('|').map((e) { + if (e.contains(':')) { + return e.split(':').map((hex) => int.parse(hex, radix: 16)); + } + + return int.parse(e, radix: 16); + }).fold([], (List acc, element) { + if (element is List) { + for (var i = element[0] as int; i <= (element[1] as int); i++) {} + } else if (element is int) { + acc.add(element); + } + + return acc; + }).toList(); +} + +String removeCombiningCharacters(String source) { + return source + .split('') + .where((char) => !COMBININGCODEPOINTS.contains(char.codeUnits.first)) + .join(''); +} + +bool isCJK(String char) { + final n = char.codeUnitAt(0); + + for (var x in CJKINTERVALS) { + final imin = x[0] as num; + final imax = x[1] as num; + + if (n >= imin && n <= imax) return true; + } + + return false; +} + +String removeCJKSpaces(String source) { + final splitted = source.split(''); + final filtered = []; + + for (var i = 0; i < splitted.length; i++) { + final char = splitted[i]; + final isSpace = char.trim() == ''; + final prevIsCJK = i != 0 && isCJK(splitted[i - 1]); + final nextIsCJK = i != splitted.length - 1 && isCJK(splitted[i + 1]); + + if (!(isSpace && prevIsCJK && nextIsCJK)) { + filtered.add(char); + } + } + + return filtered.join(''); +} + +String normalizeText(String source) { + final res = removeCombiningCharacters(unorm.nfkd(source).toLowerCase()) + .trim() + .split('/\s+/') + .join(' '); + + return removeCJKSpaces(res); +} + +const CJKINTERVALS = [ + [0x4e00, 0x9fff, 'CJK Unified Ideographs'], + [0x3400, 0x4dbf, 'CJK Unified Ideographs Extension A'], + [0x20000, 0x2a6df, 'CJK Unified Ideographs Extension B'], + [0x2a700, 0x2b73f, 'CJK Unified Ideographs Extension C'], + [0x2b740, 0x2b81f, 'CJK Unified Ideographs Extension D'], + [0xf900, 0xfaff, 'CJK Compatibility Ideographs'], + [0x2f800, 0x2fa1d, 'CJK Compatibility Ideographs Supplement'], + [0x3190, 0x319f, 'Kanbun'], + [0x2e80, 0x2eff, 'CJK Radicals Supplement'], + [0x2f00, 0x2fdf, 'CJK Radicals'], + [0x31c0, 0x31ef, 'CJK Strokes'], + [0x2ff0, 0x2fff, 'Ideographic Description Characters'], + [0xe0100, 0xe01ef, 'Variation Selectors Supplement'], + [0x3100, 0x312f, 'Bopomofo'], + [0x31a0, 0x31bf, 'Bopomofo Extended'], + [0xff00, 0xffef, 'Halfwidth and Fullwidth Forms'], + [0x3040, 0x309f, 'Hiragana'], + [0x30a0, 0x30ff, 'Katakana'], + [0x31f0, 0x31ff, 'Katakana Phonetic Extensions'], + [0x1b000, 0x1b0ff, 'Kana Supplement'], + [0xac00, 0xd7af, 'Hangul Syllables'], + [0x1100, 0x11ff, 'Hangul Jamo'], + [0xa960, 0xa97f, 'Hangul Jamo Extended A'], + [0xd7b0, 0xd7ff, 'Hangul Jamo Extended B'], + [0x3130, 0x318f, 'Hangul Compatibility Jamo'], + [0xa4d0, 0xa4ff, 'Lisu'], + [0x16f00, 0x16f9f, 'Miao'], + [0xa000, 0xa48f, 'Yi Syllables'], + [0xa490, 0xa4cf, 'Yi Radicals'], +]; + +final englishWordlist = [ + 'abandon', + 'ability', + 'able', + 'about', + 'above', + 'absent', + 'absorb', + 'abstract', + 'absurd', + 'abuse', + 'access', + 'accident', + 'account', + 'accuse', + 'achieve', + 'acid', + 'acoustic', + 'acquire', + 'across', + 'act', + 'action', + 'actor', + 'actress', + 'actual', + 'adapt', + 'add', + 'addict', + 'address', + 'adjust', + 'admit', + 'adult', + 'advance', + 'advice', + 'aerobic', + 'affair', + 'afford', + 'afraid', + 'again', + 'age', + 'agent', + 'agree', + 'ahead', + 'aim', + 'air', + 'airport', + 'aisle', + 'alarm', + 'album', + 'alcohol', + 'alert', + 'alien', + 'all', + 'alley', + 'allow', + 'almost', + 'alone', + 'alpha', + 'already', + 'also', + 'alter', + 'always', + 'amateur', + 'amazing', + 'among', + 'amount', + 'amused', + 'analyst', + 'anchor', + 'ancient', + 'anger', + 'angle', + 'angry', + 'animal', + 'ankle', + 'announce', + 'annual', + 'another', + 'answer', + 'antenna', + 'antique', + 'anxiety', + 'any', + 'apart', + 'apology', + 'appear', + 'apple', + 'approve', + 'april', + 'arch', + 'arctic', + 'area', + 'arena', + 'argue', + 'arm', + 'armed', + 'armor', + 'army', + 'around', + 'arrange', + 'arrest', + 'arrive', + 'arrow', + 'art', + 'artefact', + 'artist', + 'artwork', + 'ask', + 'aspect', + 'assault', + 'asset', + 'assist', + 'assume', + 'asthma', + 'athlete', + 'atom', + 'attack', + 'attend', + 'attitude', + 'attract', + 'auction', + 'audit', + 'august', + 'aunt', + 'author', + 'auto', + 'autumn', + 'average', + 'avocado', + 'avoid', + 'awake', + 'aware', + 'away', + 'awesome', + 'awful', + 'awkward', + 'axis', + 'baby', + 'bachelor', + 'bacon', + 'badge', + 'bag', + 'balance', + 'balcony', + 'ball', + 'bamboo', + 'banana', + 'banner', + 'bar', + 'barely', + 'bargain', + 'barrel', + 'base', + 'basic', + 'basket', + 'battle', + 'beach', + 'bean', + 'beauty', + 'because', + 'become', + 'beef', + 'before', + 'begin', + 'behave', + 'behind', + 'believe', + 'below', + 'belt', + 'bench', + 'benefit', + 'best', + 'betray', + 'better', + 'between', + 'beyond', + 'bicycle', + 'bid', + 'bike', + 'bind', + 'biology', + 'bird', + 'birth', + 'bitter', + 'black', + 'blade', + 'blame', + 'blanket', + 'blast', + 'bleak', + 'bless', + 'blind', + 'blood', + 'blossom', + 'blouse', + 'blue', + 'blur', + 'blush', + 'board', + 'boat', + 'body', + 'boil', + 'bomb', + 'bone', + 'bonus', + 'book', + 'boost', + 'border', + 'boring', + 'borrow', + 'boss', + 'bottom', + 'bounce', + 'box', + 'boy', + 'bracket', + 'brain', + 'brand', + 'brass', + 'brave', + 'bread', + 'breeze', + 'brick', + 'bridge', + 'brief', + 'bright', + 'bring', + 'brisk', + 'broccoli', + 'broken', + 'bronze', + 'broom', + 'brother', + 'brown', + 'brush', + 'bubble', + 'buddy', + 'budget', + 'buffalo', + 'build', + 'bulb', + 'bulk', + 'bullet', + 'bundle', + 'bunker', + 'burden', + 'burger', + 'burst', + 'bus', + 'business', + 'busy', + 'butter', + 'buyer', + 'buzz', + 'cabbage', + 'cabin', + 'cable', + 'cactus', + 'cage', + 'cake', + 'call', + 'calm', + 'camera', + 'camp', + 'can', + 'canal', + 'cancel', + 'candy', + 'cannon', + 'canoe', + 'canvas', + 'canyon', + 'capable', + 'capital', + 'captain', + 'car', + 'carbon', + 'card', + 'cargo', + 'carpet', + 'carry', + 'cart', + 'case', + 'cash', + 'casino', + 'castle', + 'casual', + 'cat', + 'catalog', + 'catch', + 'category', + 'cattle', + 'caught', + 'cause', + 'caution', + 'cave', + 'ceiling', + 'celery', + 'cement', + 'census', + 'century', + 'cereal', + 'certain', + 'chair', + 'chalk', + 'champion', + 'change', + 'chaos', + 'chapter', + 'charge', + 'chase', + 'chat', + 'cheap', + 'check', + 'cheese', + 'chef', + 'cherry', + 'chest', + 'chicken', + 'chief', + 'child', + 'chimney', + 'choice', + 'choose', + 'chronic', + 'chuckle', + 'chunk', + 'churn', + 'cigar', + 'cinnamon', + 'circle', + 'citizen', + 'city', + 'civil', + 'claim', + 'clap', + 'clarify', + 'claw', + 'clay', + 'clean', + 'clerk', + 'clever', + 'click', + 'client', + 'cliff', + 'climb', + 'clinic', + 'clip', + 'clock', + 'clog', + 'close', + 'cloth', + 'cloud', + 'clown', + 'club', + 'clump', + 'cluster', + 'clutch', + 'coach', + 'coast', + 'coconut', + 'code', + 'coffee', + 'coil', + 'coin', + 'collect', + 'color', + 'column', + 'combine', + 'come', + 'comfort', + 'comic', + 'common', + 'company', + 'concert', + 'conduct', + 'confirm', + 'congress', + 'connect', + 'consider', + 'control', + 'convince', + 'cook', + 'cool', + 'copper', + 'copy', + 'coral', + 'core', + 'corn', + 'correct', + 'cost', + 'cotton', + 'couch', + 'country', + 'couple', + 'course', + 'cousin', + 'cover', + 'coyote', + 'crack', + 'cradle', + 'craft', + 'cram', + 'crane', + 'crash', + 'crater', + 'crawl', + 'crazy', + 'cream', + 'credit', + 'creek', + 'crew', + 'cricket', + 'crime', + 'crisp', + 'critic', + 'crop', + 'cross', + 'crouch', + 'crowd', + 'crucial', + 'cruel', + 'cruise', + 'crumble', + 'crunch', + 'crush', + 'cry', + 'crystal', + 'cube', + 'culture', + 'cup', + 'cupboard', + 'curious', + 'current', + 'curtain', + 'curve', + 'cushion', + 'custom', + 'cute', + 'cycle', + 'dad', + 'damage', + 'damp', + 'dance', + 'danger', + 'daring', + 'dash', + 'daughter', + 'dawn', + 'day', + 'deal', + 'debate', + 'debris', + 'decade', + 'december', + 'decide', + 'decline', + 'decorate', + 'decrease', + 'deer', + 'defense', + 'define', + 'defy', + 'degree', + 'delay', + 'deliver', + 'demand', + 'demise', + 'denial', + 'dentist', + 'deny', + 'depart', + 'depend', + 'deposit', + 'depth', + 'deputy', + 'derive', + 'describe', + 'desert', + 'design', + 'desk', + 'despair', + 'destroy', + 'detail', + 'detect', + 'develop', + 'device', + 'devote', + 'diagram', + 'dial', + 'diamond', + 'diary', + 'dice', + 'diesel', + 'diet', + 'differ', + 'digital', + 'dignity', + 'dilemma', + 'dinner', + 'dinosaur', + 'direct', + 'dirt', + 'disagree', + 'discover', + 'disease', + 'dish', + 'dismiss', + 'disorder', + 'display', + 'distance', + 'divert', + 'divide', + 'divorce', + 'dizzy', + 'doctor', + 'document', + 'dog', + 'doll', + 'dolphin', + 'domain', + 'donate', + 'donkey', + 'donor', + 'door', + 'dose', + 'double', + 'dove', + 'draft', + 'dragon', + 'drama', + 'drastic', + 'draw', + 'dream', + 'dress', + 'drift', + 'drill', + 'drink', + 'drip', + 'drive', + 'drop', + 'drum', + 'dry', + 'duck', + 'dumb', + 'dune', + 'during', + 'dust', + 'dutch', + 'duty', + 'dwarf', + 'dynamic', + 'eager', + 'eagle', + 'early', + 'earn', + 'earth', + 'easily', + 'east', + 'easy', + 'echo', + 'ecology', + 'economy', + 'edge', + 'edit', + 'educate', + 'effort', + 'egg', + 'eight', + 'either', + 'elbow', + 'elder', + 'electric', + 'elegant', + 'element', + 'elephant', + 'elevator', + 'elite', + 'else', + 'embark', + 'embody', + 'embrace', + 'emerge', + 'emotion', + 'employ', + 'empower', + 'empty', + 'enable', + 'enact', + 'end', + 'endless', + 'endorse', + 'enemy', + 'energy', + 'enforce', + 'engage', + 'engine', + 'enhance', + 'enjoy', + 'enlist', + 'enough', + 'enrich', + 'enroll', + 'ensure', + 'enter', + 'entire', + 'entry', + 'envelope', + 'episode', + 'equal', + 'equip', + 'era', + 'erase', + 'erode', + 'erosion', + 'error', + 'erupt', + 'escape', + 'essay', + 'essence', + 'estate', + 'eternal', + 'ethics', + 'evidence', + 'evil', + 'evoke', + 'evolve', + 'exact', + 'example', + 'excess', + 'exchange', + 'excite', + 'exclude', + 'excuse', + 'execute', + 'exercise', + 'exhaust', + 'exhibit', + 'exile', + 'exist', + 'exit', + 'exotic', + 'expand', + 'expect', + 'expire', + 'explain', + 'expose', + 'express', + 'extend', + 'extra', + 'eye', + 'eyebrow', + 'fabric', + 'face', + 'faculty', + 'fade', + 'faint', + 'faith', + 'fall', + 'false', + 'fame', + 'family', + 'famous', + 'fan', + 'fancy', + 'fantasy', + 'farm', + 'fashion', + 'fat', + 'fatal', + 'father', + 'fatigue', + 'fault', + 'favorite', + 'feature', + 'february', + 'federal', + 'fee', + 'feed', + 'feel', + 'female', + 'fence', + 'festival', + 'fetch', + 'fever', + 'few', + 'fiber', + 'fiction', + 'field', + 'figure', + 'file', + 'film', + 'filter', + 'final', + 'find', + 'fine', + 'finger', + 'finish', + 'fire', + 'firm', + 'first', + 'fiscal', + 'fish', + 'fit', + 'fitness', + 'fix', + 'flag', + 'flame', + 'flash', + 'flat', + 'flavor', + 'flee', + 'flight', + 'flip', + 'float', + 'flock', + 'floor', + 'flower', + 'fluid', + 'flush', + 'fly', + 'foam', + 'focus', + 'fog', + 'foil', + 'fold', + 'follow', + 'food', + 'foot', + 'force', + 'forest', + 'forget', + 'fork', + 'fortune', + 'forum', + 'forward', + 'fossil', + 'foster', + 'found', + 'fox', + 'fragile', + 'frame', + 'frequent', + 'fresh', + 'friend', + 'fringe', + 'frog', + 'front', + 'frost', + 'frown', + 'frozen', + 'fruit', + 'fuel', + 'fun', + 'funny', + 'furnace', + 'fury', + 'future', + 'gadget', + 'gain', + 'galaxy', + 'gallery', + 'game', + 'gap', + 'garage', + 'garbage', + 'garden', + 'garlic', + 'garment', + 'gas', + 'gasp', + 'gate', + 'gather', + 'gauge', + 'gaze', + 'general', + 'genius', + 'genre', + 'gentle', + 'genuine', + 'gesture', + 'ghost', + 'giant', + 'gift', + 'giggle', + 'ginger', + 'giraffe', + 'girl', + 'give', + 'glad', + 'glance', + 'glare', + 'glass', + 'glide', + 'glimpse', + 'globe', + 'gloom', + 'glory', + 'glove', + 'glow', + 'glue', + 'goat', + 'goddess', + 'gold', + 'good', + 'goose', + 'gorilla', + 'gospel', + 'gossip', + 'govern', + 'gown', + 'grab', + 'grace', + 'grain', + 'grant', + 'grape', + 'grass', + 'gravity', + 'great', + 'green', + 'grid', + 'grief', + 'grit', + 'grocery', + 'group', + 'grow', + 'grunt', + 'guard', + 'guess', + 'guide', + 'guilt', + 'guitar', + 'gun', + 'gym', + 'habit', + 'hair', + 'half', + 'hammer', + 'hamster', + 'hand', + 'happy', + 'harbor', + 'hard', + 'harsh', + 'harvest', + 'hat', + 'have', + 'hawk', + 'hazard', + 'head', + 'health', + 'heart', + 'heavy', + 'hedgehog', + 'height', + 'hello', + 'helmet', + 'help', + 'hen', + 'hero', + 'hidden', + 'high', + 'hill', + 'hint', + 'hip', + 'hire', + 'history', + 'hobby', + 'hockey', + 'hold', + 'hole', + 'holiday', + 'hollow', + 'home', + 'honey', + 'hood', + 'hope', + 'horn', + 'horror', + 'horse', + 'hospital', + 'host', + 'hotel', + 'hour', + 'hover', + 'hub', + 'huge', + 'human', + 'humble', + 'humor', + 'hundred', + 'hungry', + 'hunt', + 'hurdle', + 'hurry', + 'hurt', + 'husband', + 'hybrid', + 'ice', + 'icon', + 'idea', + 'identify', + 'idle', + 'ignore', + 'ill', + 'illegal', + 'illness', + 'image', + 'imitate', + 'immense', + 'immune', + 'impact', + 'impose', + 'improve', + 'impulse', + 'inch', + 'include', + 'income', + 'increase', + 'index', + 'indicate', + 'indoor', + 'industry', + 'infant', + 'inflict', + 'inform', + 'inhale', + 'inherit', + 'initial', + 'inject', + 'injury', + 'inmate', + 'inner', + 'innocent', + 'input', + 'inquiry', + 'insane', + 'insect', + 'inside', + 'inspire', + 'install', + 'intact', + 'interest', + 'into', + 'invest', + 'invite', + 'involve', + 'iron', + 'island', + 'isolate', + 'issue', + 'item', + 'ivory', + 'jacket', + 'jaguar', + 'jar', + 'jazz', + 'jealous', + 'jeans', + 'jelly', + 'jewel', + 'job', + 'join', + 'joke', + 'journey', + 'joy', + 'judge', + 'juice', + 'jump', + 'jungle', + 'junior', + 'junk', + 'just', + 'kangaroo', + 'keen', + 'keep', + 'ketchup', + 'key', + 'kick', + 'kid', + 'kidney', + 'kind', + 'kingdom', + 'kiss', + 'kit', + 'kitchen', + 'kite', + 'kitten', + 'kiwi', + 'knee', + 'knife', + 'knock', + 'know', + 'lab', + 'label', + 'labor', + 'ladder', + 'lady', + 'lake', + 'lamp', + 'language', + 'laptop', + 'large', + 'later', + 'latin', + 'laugh', + 'laundry', + 'lava', + 'law', + 'lawn', + 'lawsuit', + 'layer', + 'lazy', + 'leader', + 'leaf', + 'learn', + 'leave', + 'lecture', + 'left', + 'leg', + 'legal', + 'legend', + 'leisure', + 'lemon', + 'lend', + 'length', + 'lens', + 'leopard', + 'lesson', + 'letter', + 'level', + 'liar', + 'liberty', + 'library', + 'license', + 'life', + 'lift', + 'light', + 'like', + 'limb', + 'limit', + 'link', + 'lion', + 'liquid', + 'list', + 'little', + 'live', + 'lizard', + 'load', + 'loan', + 'lobster', + 'local', + 'lock', + 'logic', + 'lonely', + 'long', + 'loop', + 'lottery', + 'loud', + 'lounge', + 'love', + 'loyal', + 'lucky', + 'luggage', + 'lumber', + 'lunar', + 'lunch', + 'luxury', + 'lyrics', + 'machine', + 'mad', + 'magic', + 'magnet', + 'maid', + 'mail', + 'main', + 'major', + 'make', + 'mammal', + 'man', + 'manage', + 'mandate', + 'mango', + 'mansion', + 'manual', + 'maple', + 'marble', + 'march', + 'margin', + 'marine', + 'market', + 'marriage', + 'mask', + 'mass', + 'master', + 'match', + 'material', + 'math', + 'matrix', + 'matter', + 'maximum', + 'maze', + 'meadow', + 'mean', + 'measure', + 'meat', + 'mechanic', + 'medal', + 'media', + 'melody', + 'melt', + 'member', + 'memory', + 'mention', + 'menu', + 'mercy', + 'merge', + 'merit', + 'merry', + 'mesh', + 'message', + 'metal', + 'method', + 'middle', + 'midnight', + 'milk', + 'million', + 'mimic', + 'mind', + 'minimum', + 'minor', + 'minute', + 'miracle', + 'mirror', + 'misery', + 'miss', + 'mistake', + 'mix', + 'mixed', + 'mixture', + 'mobile', + 'model', + 'modify', + 'mom', + 'moment', + 'monitor', + 'monkey', + 'monster', + 'month', + 'moon', + 'moral', + 'more', + 'morning', + 'mosquito', + 'mother', + 'motion', + 'motor', + 'mountain', + 'mouse', + 'move', + 'movie', + 'much', + 'muffin', + 'mule', + 'multiply', + 'muscle', + 'museum', + 'mushroom', + 'music', + 'must', + 'mutual', + 'myself', + 'mystery', + 'myth', + 'naive', + 'name', + 'napkin', + 'narrow', + 'nasty', + 'nation', + 'nature', + 'near', + 'neck', + 'need', + 'negative', + 'neglect', + 'neither', + 'nephew', + 'nerve', + 'nest', + 'net', + 'network', + 'neutral', + 'never', + 'news', + 'next', + 'nice', + 'night', + 'noble', + 'noise', + 'nominee', + 'noodle', + 'normal', + 'north', + 'nose', + 'notable', + 'note', + 'nothing', + 'notice', + 'novel', + 'now', + 'nuclear', + 'number', + 'nurse', + 'nut', + 'oak', + 'obey', + 'object', + 'oblige', + 'obscure', + 'observe', + 'obtain', + 'obvious', + 'occur', + 'ocean', + 'october', + 'odor', + 'off', + 'offer', + 'office', + 'often', + 'oil', + 'okay', + 'old', + 'olive', + 'olympic', + 'omit', + 'once', + 'one', + 'onion', + 'online', + 'only', + 'open', + 'opera', + 'opinion', + 'oppose', + 'option', + 'orange', + 'orbit', + 'orchard', + 'order', + 'ordinary', + 'organ', + 'orient', + 'original', + 'orphan', + 'ostrich', + 'other', + 'outdoor', + 'outer', + 'output', + 'outside', + 'oval', + 'oven', + 'over', + 'own', + 'owner', + 'oxygen', + 'oyster', + 'ozone', + 'pact', + 'paddle', + 'page', + 'pair', + 'palace', + 'palm', + 'panda', + 'panel', + 'panic', + 'panther', + 'paper', + 'parade', + 'parent', + 'park', + 'parrot', + 'party', + 'pass', + 'patch', + 'path', + 'patient', + 'patrol', + 'pattern', + 'pause', + 'pave', + 'payment', + 'peace', + 'peanut', + 'pear', + 'peasant', + 'pelican', + 'pen', + 'penalty', + 'pencil', + 'people', + 'pepper', + 'perfect', + 'permit', + 'person', + 'pet', + 'phone', + 'photo', + 'phrase', + 'physical', + 'piano', + 'picnic', + 'picture', + 'piece', + 'pig', + 'pigeon', + 'pill', + 'pilot', + 'pink', + 'pioneer', + 'pipe', + 'pistol', + 'pitch', + 'pizza', + 'place', + 'planet', + 'plastic', + 'plate', + 'play', + 'please', + 'pledge', + 'pluck', + 'plug', + 'plunge', + 'poem', + 'poet', + 'point', + 'polar', + 'pole', + 'police', + 'pond', + 'pony', + 'pool', + 'popular', + 'portion', + 'position', + 'possible', + 'post', + 'potato', + 'pottery', + 'poverty', + 'powder', + 'power', + 'practice', + 'praise', + 'predict', + 'prefer', + 'prepare', + 'present', + 'pretty', + 'prevent', + 'price', + 'pride', + 'primary', + 'print', + 'priority', + 'prison', + 'private', + 'prize', + 'problem', + 'process', + 'produce', + 'profit', + 'program', + 'project', + 'promote', + 'proof', + 'property', + 'prosper', + 'protect', + 'proud', + 'provide', + 'public', + 'pudding', + 'pull', + 'pulp', + 'pulse', + 'pumpkin', + 'punch', + 'pupil', + 'puppy', + 'purchase', + 'purity', + 'purpose', + 'purse', + 'push', + 'put', + 'puzzle', + 'pyramid', + 'quality', + 'quantum', + 'quarter', + 'question', + 'quick', + 'quit', + 'quiz', + 'quote', + 'rabbit', + 'raccoon', + 'race', + 'rack', + 'radar', + 'radio', + 'rail', + 'rain', + 'raise', + 'rally', + 'ramp', + 'ranch', + 'random', + 'range', + 'rapid', + 'rare', + 'rate', + 'rather', + 'raven', + 'raw', + 'razor', + 'ready', + 'real', + 'reason', + 'rebel', + 'rebuild', + 'recall', + 'receive', + 'recipe', + 'record', + 'recycle', + 'reduce', + 'reflect', + 'reform', + 'refuse', + 'region', + 'regret', + 'regular', + 'reject', + 'relax', + 'release', + 'relief', + 'rely', + 'remain', + 'remember', + 'remind', + 'remove', + 'render', + 'renew', + 'rent', + 'reopen', + 'repair', + 'repeat', + 'replace', + 'report', + 'require', + 'rescue', + 'resemble', + 'resist', + 'resource', + 'response', + 'result', + 'retire', + 'retreat', + 'return', + 'reunion', + 'reveal', + 'review', + 'reward', + 'rhythm', + 'rib', + 'ribbon', + 'rice', + 'rich', + 'ride', + 'ridge', + 'rifle', + 'right', + 'rigid', + 'ring', + 'riot', + 'ripple', + 'risk', + 'ritual', + 'rival', + 'river', + 'road', + 'roast', + 'robot', + 'robust', + 'rocket', + 'romance', + 'roof', + 'rookie', + 'room', + 'rose', + 'rotate', + 'rough', + 'round', + 'route', + 'royal', + 'rubber', + 'rude', + 'rug', + 'rule', + 'run', + 'runway', + 'rural', + 'sad', + 'saddle', + 'sadness', + 'safe', + 'sail', + 'salad', + 'salmon', + 'salon', + 'salt', + 'salute', + 'same', + 'sample', + 'sand', + 'satisfy', + 'satoshi', + 'sauce', + 'sausage', + 'save', + 'say', + 'scale', + 'scan', + 'scare', + 'scatter', + 'scene', + 'scheme', + 'school', + 'science', + 'scissors', + 'scorpion', + 'scout', + 'scrap', + 'screen', + 'script', + 'scrub', + 'sea', + 'search', + 'season', + 'seat', + 'second', + 'secret', + 'section', + 'security', + 'seed', + 'seek', + 'segment', + 'select', + 'sell', + 'seminar', + 'senior', + 'sense', + 'sentence', + 'series', + 'service', + 'session', + 'settle', + 'setup', + 'seven', + 'shadow', + 'shaft', + 'shallow', + 'share', + 'shed', + 'shell', + 'sheriff', + 'shield', + 'shift', + 'shine', + 'ship', + 'shiver', + 'shock', + 'shoe', + 'shoot', + 'shop', + 'short', + 'shoulder', + 'shove', + 'shrimp', + 'shrug', + 'shuffle', + 'shy', + 'sibling', + 'sick', + 'side', + 'siege', + 'sight', + 'sign', + 'silent', + 'silk', + 'silly', + 'silver', + 'similar', + 'simple', + 'since', + 'sing', + 'siren', + 'sister', + 'situate', + 'six', + 'size', + 'skate', + 'sketch', + 'ski', + 'skill', + 'skin', + 'skirt', + 'skull', + 'slab', + 'slam', + 'sleep', + 'slender', + 'slice', + 'slide', + 'slight', + 'slim', + 'slogan', + 'slot', + 'slow', + 'slush', + 'small', + 'smart', + 'smile', + 'smoke', + 'smooth', + 'snack', + 'snake', + 'snap', + 'sniff', + 'snow', + 'soap', + 'soccer', + 'social', + 'sock', + 'soda', + 'soft', + 'solar', + 'soldier', + 'solid', + 'solution', + 'solve', + 'someone', + 'song', + 'soon', + 'sorry', + 'sort', + 'soul', + 'sound', + 'soup', + 'source', + 'south', + 'space', + 'spare', + 'spatial', + 'spawn', + 'speak', + 'special', + 'speed', + 'spell', + 'spend', + 'sphere', + 'spice', + 'spider', + 'spike', + 'spin', + 'spirit', + 'split', + 'spoil', + 'sponsor', + 'spoon', + 'sport', + 'spot', + 'spray', + 'spread', + 'spring', + 'spy', + 'square', + 'squeeze', + 'squirrel', + 'stable', + 'stadium', + 'staff', + 'stage', + 'stairs', + 'stamp', + 'stand', + 'start', + 'state', + 'stay', + 'steak', + 'steel', + 'stem', + 'step', + 'stereo', + 'stick', + 'still', + 'sting', + 'stock', + 'stomach', + 'stone', + 'stool', + 'story', + 'stove', + 'strategy', + 'street', + 'strike', + 'strong', + 'struggle', + 'student', + 'stuff', + 'stumble', + 'style', + 'subject', + 'submit', + 'subway', + 'success', + 'such', + 'sudden', + 'suffer', + 'sugar', + 'suggest', + 'suit', + 'summer', + 'sun', + 'sunny', + 'sunset', + 'super', + 'supply', + 'supreme', + 'sure', + 'surface', + 'surge', + 'surprise', + 'surround', + 'survey', + 'suspect', + 'sustain', + 'swallow', + 'swamp', + 'swap', + 'swarm', + 'swear', + 'sweet', + 'swift', + 'swim', + 'swing', + 'switch', + 'sword', + 'symbol', + 'symptom', + 'syrup', + 'system', + 'table', + 'tackle', + 'tag', + 'tail', + 'talent', + 'talk', + 'tank', + 'tape', + 'target', + 'task', + 'taste', + 'tattoo', + 'taxi', + 'teach', + 'team', + 'tell', + 'ten', + 'tenant', + 'tennis', + 'tent', + 'term', + 'test', + 'text', + 'thank', + 'that', + 'theme', + 'then', + 'theory', + 'there', + 'they', + 'thing', + 'this', + 'thought', + 'three', + 'thrive', + 'throw', + 'thumb', + 'thunder', + 'ticket', + 'tide', + 'tiger', + 'tilt', + 'timber', + 'time', + 'tiny', + 'tip', + 'tired', + 'tissue', + 'title', + 'toast', + 'tobacco', + 'today', + 'toddler', + 'toe', + 'together', + 'toilet', + 'token', + 'tomato', + 'tomorrow', + 'tone', + 'tongue', + 'tonight', + 'tool', + 'tooth', + 'top', + 'topic', + 'topple', + 'torch', + 'tornado', + 'tortoise', + 'toss', + 'total', + 'tourist', + 'toward', + 'tower', + 'town', + 'toy', + 'track', + 'trade', + 'traffic', + 'tragic', + 'train', + 'transfer', + 'trap', + 'trash', + 'travel', + 'tray', + 'treat', + 'tree', + 'trend', + 'trial', + 'tribe', + 'trick', + 'trigger', + 'trim', + 'trip', + 'trophy', + 'trouble', + 'truck', + 'true', + 'truly', + 'trumpet', + 'trust', + 'truth', + 'try', + 'tube', + 'tuition', + 'tumble', + 'tuna', + 'tunnel', + 'turkey', + 'turn', + 'turtle', + 'twelve', + 'twenty', + 'twice', + 'twin', + 'twist', + 'two', + 'type', + 'typical', + 'ugly', + 'umbrella', + 'unable', + 'unaware', + 'uncle', + 'uncover', + 'under', + 'undo', + 'unfair', + 'unfold', + 'unhappy', + 'uniform', + 'unique', + 'unit', + 'universe', + 'unknown', + 'unlock', + 'until', + 'unusual', + 'unveil', + 'update', + 'upgrade', + 'uphold', + 'upon', + 'upper', + 'upset', + 'urban', + 'urge', + 'usage', + 'use', + 'used', + 'useful', + 'useless', + 'usual', + 'utility', + 'vacant', + 'vacuum', + 'vague', + 'valid', + 'valley', + 'valve', + 'van', + 'vanish', + 'vapor', + 'various', + 'vast', + 'vault', + 'vehicle', + 'velvet', + 'vendor', + 'venture', + 'venue', + 'verb', + 'verify', + 'version', + 'very', + 'vessel', + 'veteran', + 'viable', + 'vibrant', + 'vicious', + 'victory', + 'video', + 'view', + 'village', + 'vintage', + 'violin', + 'virtual', + 'virus', + 'visa', + 'visit', + 'visual', + 'vital', + 'vivid', + 'vocal', + 'voice', + 'void', + 'volcano', + 'volume', + 'vote', + 'voyage', + 'wage', + 'wagon', + 'wait', + 'walk', + 'wall', + 'walnut', + 'want', + 'warfare', + 'warm', + 'warrior', + 'wash', + 'wasp', + 'waste', + 'water', + 'wave', + 'way', + 'wealth', + 'weapon', + 'wear', + 'weasel', + 'weather', + 'web', + 'wedding', + 'weekend', + 'weird', + 'welcome', + 'west', + 'wet', + 'whale', + 'what', + 'wheat', + 'wheel', + 'when', + 'where', + 'whip', + 'whisper', + 'wide', + 'width', + 'wife', + 'wild', + 'will', + 'win', + 'window', + 'wine', + 'wing', + 'wink', + 'winner', + 'winter', + 'wire', + 'wisdom', + 'wise', + 'wish', + 'witness', + 'wolf', + 'woman', + 'wonder', + 'wood', + 'wool', + 'word', + 'work', + 'world', + 'worry', + 'worth', + 'wrap', + 'wreck', + 'wrestle', + 'wrist', + 'write', + 'wrong', + 'yard', + 'year', + 'yellow', + 'you', + 'young', + 'youth', + 'zebra', + 'zero', + 'zone', + 'zoo' +]; diff --git a/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart b/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart new file mode 100644 index 000000000..761b02601 --- /dev/null +++ b/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart @@ -0,0 +1,5 @@ +class BitcoinMnemonicIsIncorrectException implements Exception { + @override + String toString() => + 'Bitcoin mnemonic has incorrect format. Mnemonic should contain 12 words separated by space.'; +} diff --git a/lib/bitcoin/bitcoin_transaction_credentials.dart b/lib/bitcoin/bitcoin_transaction_credentials.dart index 9e64634b3..40f7a7aa7 100644 --- a/lib/bitcoin/bitcoin_transaction_credentials.dart +++ b/lib/bitcoin/bitcoin_transaction_credentials.dart @@ -4,6 +4,6 @@ class BitcoinTransactionCredentials { BitcoinTransactionCredentials(this.address, this.amount, this.priority); final String address; - final double amount; + final String amount; TransactionPriority priority; } diff --git a/lib/bitcoin/bitcoin_transaction_history.dart b/lib/bitcoin/bitcoin_transaction_history.dart index c687c0ba4..09cefe5d4 100644 --- a/lib/bitcoin/bitcoin_transaction_history.dart +++ b/lib/bitcoin/bitcoin_transaction_history.dart @@ -65,7 +65,7 @@ abstract class BitcoinTransactionHistoryBase return historiesWithDetails.fold>( {}, (acc, tx) { - acc[tx.id] = tx; + acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx; return acc; }); } @@ -103,10 +103,6 @@ abstract class BitcoinTransactionHistoryBase Future save() async { final data = json.encode({'height': _height, 'transactions': transactions}); - - print('data'); - print(data); - await writeData(path: path, password: _password, data: data); } @@ -168,7 +164,9 @@ abstract class BitcoinTransactionHistoryBase }); _height = content['height'] as int; - } catch (_) {} + } catch (e) { + print(e); + } } void _updateOrInsert(BitcoinTransactionInfo transaction) { diff --git a/lib/bitcoin/bitcoin_transaction_info.dart b/lib/bitcoin/bitcoin_transaction_info.dart index 29cb8521e..fb1400c5e 100644 --- a/lib/bitcoin/bitcoin_transaction_info.dart +++ b/lib/bitcoin/bitcoin_transaction_info.dart @@ -12,6 +12,7 @@ class BitcoinTransactionInfo extends TransactionInfo { {@required String id, @required int height, @required int amount, + @required int fee, @required TransactionDirection direction, @required bool isPending, @required DateTime date, @@ -19,6 +20,7 @@ class BitcoinTransactionInfo extends TransactionInfo { this.id = id; this.height = height; this.amount = amount; + this.fee = fee; this.direction = direction; this.date = date; this.isPending = isPending; @@ -36,37 +38,42 @@ class BitcoinTransactionInfo extends TransactionInfo { : DateTime.now(); final confirmations = obj['confirmations'] as int ?? 0; var direction = TransactionDirection.incoming; + var inputsAmount = 0; + var amount = 0; + var totalOutAmount = 0; for (dynamic vin in vins) { final vout = vin['vout'] as int; final out = vin['tx']['vout'][vout] as Map; final outAddresses = (out['scriptPubKey']['addresses'] as List)?.toSet(); + inputsAmount += stringDoubleToBitcoinAmount((out['value'] as double ?? 0).toString()); if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) { direction = TransactionDirection.outgoing; - break; } } - final amount = vout.fold(0, (int acc, dynamic out) { + for (dynamic out in vout) { final outAddresses = out['scriptPubKey']['addresses'] as List ?? []; final ntrs = outAddresses.toSet().intersection(addressesSet); - var amount = acc; + final value = stringDoubleToBitcoinAmount((out['value'] as double ?? 0.0).toString()); + totalOutAmount += value; if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) || (direction == TransactionDirection.outgoing && ntrs.isEmpty)) { - amount += doubleToBitcoinAmount(out['value'] as double ?? 0.0); + amount += value; } + } - return amount; - }); + final fee = inputsAmount - totalOutAmount; return BitcoinTransactionInfo( id: id, height: height, isPending: false, + fee: fee, direction: direction, amount: amount, date: date, @@ -101,6 +108,7 @@ class BitcoinTransactionInfo extends TransactionInfo { id: tx.getId(), height: height, isPending: false, + fee: null, direction: TransactionDirection.incoming, amount: amount, date: date, @@ -112,6 +120,7 @@ class BitcoinTransactionInfo extends TransactionInfo { id: data['id'] as String, height: data['height'] as int, amount: data['amount'] as int, + fee: data['fee'] as int, direction: parseTransactionDirectionFromInt(data['direction'] as int), date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int), isPending: data['isPending'] as bool, @@ -124,12 +133,29 @@ class BitcoinTransactionInfo extends TransactionInfo { String amountFormatted() => '${formatAmount(bitcoinAmountToString(amount: amount))} BTC'; + @override + String feeFormatted() => fee != null + ? '${formatAmount(bitcoinAmountToString(amount: fee))} BTC' + : ''; + @override String fiatAmount() => _fiatAmount ?? ''; @override void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); + BitcoinTransactionInfo updated(BitcoinTransactionInfo info) { + return BitcoinTransactionInfo( + id: id, + height: info.height, + amount: info.amount, + fee: info.fee, + direction: direction ?? info.direction, + date: date ?? info.date, + isPending: isPending ?? info.isPending, + confirmations: info.confirmations); + } + Map toJson() { final m = {}; m['id'] = id; @@ -139,6 +165,7 @@ class BitcoinTransactionInfo extends TransactionInfo { m['date'] = date.millisecondsSinceEpoch; m['isPending'] = isPending; m['confirmations'] = confirmations; + m['fee'] = fee; return m; } } diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index e43c0aeaa..e5c29e240 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -1,7 +1,8 @@ import 'dart:async'; import 'dart:convert'; +import 'package:cake_wallet/bitcoin/address_to_output_script.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; import 'package:mobx/mobx.dart'; -import 'package:bip39/bip39.dart' as bip39; import 'package:flutter/foundation.dart'; import 'package:rxdart/rxdart.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; @@ -42,10 +43,11 @@ abstract class BitcoinWalletBase extends WalletBase with Store { BitcoinBalance initialBalance}) : balance = initialBalance ?? BitcoinBalance(confirmed: 0, unconfirmed: 0), - hd = bitcoin.HDWallet.fromSeed(bip39.mnemonicToSeed(mnemonic), - network: bitcoin.bitcoin), + hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic), + network: bitcoin.bitcoin) + .derivePath("m/0'/0"), addresses = initialAddresses != null - ? ObservableList.of(initialAddresses) + ? ObservableList.of(initialAddresses.toSet()) : ObservableList(), syncStatus = NotConnectedSyncStatus(), _password = password, @@ -58,6 +60,7 @@ abstract class BitcoinWalletBase extends WalletBase with Store { {@required String password, @required String name, @required String dirPath, + @required WalletInfo walletInfo, String jsonSource}) { final data = json.decode(jsonSource) as Map; final mnemonic = data['mnemonic'] as String; @@ -83,7 +86,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { name: name, accountIndex: accountIndex, initialAddresses: addresses, - initialBalance: balance); + initialBalance: balance, + walletInfo: walletInfo); } static BitcoinWallet build( @@ -91,6 +95,7 @@ abstract class BitcoinWalletBase extends WalletBase with Store { @required String password, @required String name, @required String dirPath, + @required WalletInfo walletInfo, List initialAddresses, BitcoinBalance initialBalance, int accountIndex = 0}) { @@ -107,7 +112,21 @@ abstract class BitcoinWalletBase extends WalletBase with Store { accountIndex: accountIndex, initialAddresses: initialAddresses, initialBalance: initialBalance, - transactionHistory: history); + transactionHistory: history, + walletInfo: walletInfo); + } + + static int feeAmountForPriority(TransactionPriority priority) { + switch (priority) { + case TransactionPriority.slow: + return 6000; + case TransactionPriority.regular: + return 22080; + case TransactionPriority.fast: + return 24000; + default: + return 0; + } } @override @@ -148,21 +167,31 @@ abstract class BitcoinWalletBase extends WalletBase with Store { Map> _scripthashesUpdateSubject; Future init() async { - if (addresses.isEmpty) { - final index = 0; - addresses - .add(BitcoinAddressRecord(_getAddress(index: index), index: index)); + if (addresses.isEmpty || addresses.length < 33) { + final addressesCount = 33 - addresses.length; + await generateNewAddresses(addressesCount, startIndex: addresses.length); } - address = addresses.first.address; + address = addresses[_accountIndex].address; transactionHistory.wallet = this; await transactionHistory.init(); } - Future generateNewAddress({String label}) async { + @action + void nextAddress() { + _accountIndex += 1; + + if (_accountIndex >= addresses.length) { + _accountIndex = 0; + } + + address = addresses[_accountIndex].address; + } + + Future generateNewAddress() async { _accountIndex += 1; final address = BitcoinAddressRecord(_getAddress(index: _accountIndex), - index: _accountIndex, label: label); + index: _accountIndex); addresses.add(address); await save(); @@ -170,10 +199,24 @@ abstract class BitcoinWalletBase extends WalletBase with Store { return address; } - Future updateAddress(String address, {String label}) async { + Future> generateNewAddresses(int count, + {int startIndex = 0}) async { + final list = []; + + for (var i = startIndex; i < count + startIndex; i++) { + final address = BitcoinAddressRecord(_getAddress(index: i), index: i); + list.add(address); + } + + addresses.addAll(list); + await save(); + + return list; + } + + Future updateAddress(String address) async { for (final addr in addresses) { if (addr.address == address) { - addr.label = label; await save(); break; } @@ -185,8 +228,10 @@ abstract class BitcoinWalletBase extends WalletBase with Store { Future startSync() async { try { syncStatus = StartingSyncStatus(); - transactionHistory.updateAsync( - onFinished: () => print('transactionHistory update finished!')); + transactionHistory.updateAsync(onFinished: () { + print('transactionHistory update finished!'); + transactionHistory.save(); + }); _subscribeForUpdates(); await _updateBalance(); syncStatus = SyncedSyncStatus(); @@ -219,16 +264,20 @@ abstract class BitcoinWalletBase extends WalletBase with Store { Object credentials) async { final transactionCredentials = credentials as BitcoinTransactionCredentials; final inputs = []; - final fee = _feeMultiplier(transactionCredentials.priority); + final fee = feeAmountForPriority(transactionCredentials.priority); final amount = transactionCredentials.amount != null - ? doubleToBitcoinAmount(transactionCredentials.amount) - : balance.total - fee; + ? stringDoubleToBitcoinAmount(transactionCredentials.amount) + : balance.confirmed - fee; final totalAmount = amount + fee; final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin); - var leftAmount = totalAmount; final changeAddress = address; + var leftAmount = totalAmount; var totalInputAmount = 0; + if (totalAmount > balance.confirmed) { + throw BitcoinTransactionWrongBalanceException(); + } + final unspent = addresses.map((address) => eclient .getListUnspentWithAddress(address.address) .then((unspent) => unspent @@ -238,9 +287,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { final utxs = await unptsFutures; for (final utx in utxs) { - final inAmount = utx.value > totalAmount ? totalAmount : utx.value; - leftAmount = leftAmount - inAmount; - totalInputAmount += inAmount; + leftAmount = leftAmount - utx.value; + totalInputAmount += utx.value; inputs.add(utx); if (leftAmount <= 0) { @@ -279,7 +327,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { } }); - txb.addOutput(transactionCredentials.address, amount); + txb.addOutput( + addressToOutputScript(transactionCredentials.address), amount); if (changeValue > 0) { txb.addOutput(changeAddress, changeValue); @@ -295,7 +344,10 @@ abstract class BitcoinWalletBase extends WalletBase with Store { return PendingBitcoinTransaction(txb.build(), eclient: eclient, amount: amount, fee: fee) - ..addListener((transaction) => transactionHistory.addOne(transaction)); + ..addListener((transaction) async { + transactionHistory.addOne(transaction); + await _updateBalance(); + }); } String toJSON() => json.encode({ @@ -307,11 +359,13 @@ abstract class BitcoinWalletBase extends WalletBase with Store { @override double calculateEstimatedFee(TransactionPriority priority) => - bitcoinAmountToDouble(amount: _feeMultiplier(priority)); + bitcoinAmountToDouble(amount: feeAmountForPriority(priority)); @override - Future save() async => - await write(path: path, password: _password, data: toJSON()); + Future save() async { + await write(path: path, password: _password, data: toJSON()); + await transactionHistory.save(); + } bitcoin.ECPair keyPairFor({@required int index}) => generateKeyPair(hd: hd, index: index); @@ -321,13 +375,18 @@ abstract class BitcoinWalletBase extends WalletBase with Store { // FIXME: Unimplemented } + @override + void close() async { + await eclient.close(); + } + void _subscribeForUpdates() { scriptHashes.forEach((sh) async { await _scripthashesUpdateSubject[sh]?.close(); _scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh); _scripthashesUpdateSubject[sh].listen((event) async { - transactionHistory.updateAsync(); await _updateBalance(); + transactionHistory.updateAsync(); }); }); } @@ -352,17 +411,4 @@ abstract class BitcoinWalletBase extends WalletBase with Store { String _getAddress({@required int index}) => generateAddress(hd: hd, index: index); - - int _feeMultiplier(TransactionPriority priority) { - switch (priority) { - case TransactionPriority.slow: - return 6000; - case TransactionPriority.regular: - return 9000; - case TransactionPriority.fast: - return 15000; - default: - return 0; - } - } } diff --git a/lib/bitcoin/bitcoin_wallet_creation_credentials.dart b/lib/bitcoin/bitcoin_wallet_creation_credentials.dart index 051a5700e..f8df42df0 100644 --- a/lib/bitcoin/bitcoin_wallet_creation_credentials.dart +++ b/lib/bitcoin/bitcoin_wallet_creation_credentials.dart @@ -1,21 +1,23 @@ import 'package:cake_wallet/core/wallet_credentials.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; class BitcoinNewWalletCredentials extends WalletCredentials { - BitcoinNewWalletCredentials({String name}) : super(name: name); + BitcoinNewWalletCredentials({String name, WalletInfo walletInfo}) + : super(name: name, walletInfo: walletInfo); } class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials { BitcoinRestoreWalletFromSeedCredentials( - {String name, String password, this.mnemonic}) - : super(name: name, password: password); + {String name, String password, this.mnemonic, WalletInfo walletInfo}) + : super(name: name, password: password, walletInfo: walletInfo); final String mnemonic; } class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials { BitcoinRestoreWalletFromWIFCredentials( - {String name, String password, this.wif}) - : super(name: name, password: password); + {String name, String password, this.wif, WalletInfo walletInfo}) + : super(name: name, password: password, walletInfo: walletInfo); final String wif; } diff --git a/lib/bitcoin/bitcoin_wallet_service.dart b/lib/bitcoin/bitcoin_wallet_service.dart index 2e9a1c305..8e3035a2c 100644 --- a/lib/bitcoin/bitcoin_wallet_service.dart +++ b/lib/bitcoin/bitcoin_wallet_service.dart @@ -1,25 +1,34 @@ import 'dart:io'; -import 'package:bip39/bip39.dart' as bip39; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart'; import 'package:cake_wallet/bitcoin/file.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/entities/pathForWallet.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:hive/hive.dart'; class BitcoinWalletService extends WalletService< BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> { + BitcoinWalletService(this.walletInfoSource); + + final Box walletInfoSource; + @override Future create(BitcoinNewWalletCredentials credentials) async { final dirPath = await pathForWalletDir( type: WalletType.bitcoin, name: credentials.name); final wallet = BitcoinWalletBase.build( dirPath: dirPath, - mnemonic: bip39.generateMnemonic(), + mnemonic: generateMnemonic(), password: credentials.password, - name: credentials.name); + name: credentials.name, + walletInfo: credentials.walletInfo); await wallet.save(); await wallet.init(); @@ -37,11 +46,15 @@ class BitcoinWalletService extends WalletService< await pathForWalletDir(name: name, type: WalletType.bitcoin); final walletPath = '$walletDirPath/$name'; final walletJSONRaw = await read(path: walletPath, password: password); + final walletInfo = walletInfoSource.values.firstWhere( + (info) => info.id == WalletBase.idFor(name, WalletType.bitcoin), + orElse: () => null); final wallet = BitcoinWalletBase.fromJSON( password: password, name: name, dirPath: walletDirPath, - jsonSource: walletJSONRaw); + jsonSource: walletJSONRaw, + walletInfo: walletInfo); await wallet.init(); return wallet; @@ -62,13 +75,18 @@ class BitcoinWalletService extends WalletService< @override Future restoreFromSeed( BitcoinRestoreWalletFromSeedCredentials credentials) async { + if (!validateMnemonic(credentials.mnemonic)) { + throw BitcoinMnemonicIsIncorrectException(); + } + final dirPath = await pathForWalletDir( type: WalletType.bitcoin, name: credentials.name); final wallet = BitcoinWalletBase.build( dirPath: dirPath, name: credentials.name, password: credentials.password, - mnemonic: credentials.mnemonic); + mnemonic: credentials.mnemonic, + walletInfo: credentials.walletInfo); await wallet.save(); await wallet.init(); diff --git a/lib/bitcoin/electrum.dart b/lib/bitcoin/electrum.dart index 5348959e7..994264729 100644 --- a/lib/bitcoin/electrum.dart +++ b/lib/bitcoin/electrum.dart @@ -37,7 +37,8 @@ class ElectrumClient { ElectrumClient() : _id = 0, _isConnected = false, - _tasks = {}; + _tasks = {}, + unterminatedString = ''; static const connectionTimeout = Duration(seconds: 5); static const aliveTimerDuration = Duration(seconds: 2); @@ -49,6 +50,7 @@ class ElectrumClient { final Map _tasks; bool _isConnected; Timer _aliveTimer; + String unterminatedString; Future connectToUri(String uri) async { final splittedUri = uri.split(':'); @@ -73,26 +75,50 @@ class ElectrumClient { socket.listen((Uint8List event) { try { - final jsoned = + final response = json.decode(utf8.decode(event.toList())) as Map; - print(jsoned); - final method = jsoned['method']; - final id = jsoned['id'] as String; - final params = jsoned['result']; + _handleResponse(response); + } on FormatException catch (e) { + final msg = e.message.toLowerCase(); - if (method is String) { - _methodHandler(method: method, request: jsoned); + if (e.source is String) { + unterminatedString += e.source as String; + } + + if (msg.contains("not a subtype of type")) { + unterminatedString += e.source as String; return; } - _finish(id, params); + if (isJSONStringCorrect(unterminatedString)) { + final response = + json.decode(unterminatedString) as Map; + _handleResponse(response); + unterminatedString = ''; + } + } on TypeError catch (e) { + if (!e.toString().contains('Map')) { + return; + } + + final source = utf8.decode(event.toList()); + unterminatedString += source; + + if (isJSONStringCorrect(unterminatedString)) { + final response = + json.decode(unterminatedString) as Map; + _handleResponse(response); + unterminatedString = null; + } } catch (e) { - print(e); + print(e.toString()); } }, onError: (Object error) { print(error.toString()); _setIsConnected(false); - }, onDone: () => _setIsConnected(false)); + }, onDone: () { + _setIsConnected(false); + }); keepAlive(); } @@ -103,7 +129,7 @@ class ElectrumClient { Future ping() async { try { - // await callWithTimeout(method: 'server.ping'); + await callWithTimeout(method: 'server.ping'); _setIsConnected(true); } on RequestFailedTimeoutException catch (_) { _setIsConnected(false); @@ -209,16 +235,20 @@ class ElectrumClient { Future> getTransactionExpanded( {@required String hash}) async { - final originalTx = await getTransactionRaw(hash: hash); - final vins = originalTx['vin'] as List; + try { + final originalTx = await getTransactionRaw(hash: hash); + final vins = originalTx['vin'] as List; - for (dynamic vin in vins) { - if (vin is Map) { - vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String); + for (dynamic vin in vins) { + if (vin is Map) { + vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String); + } } - } - return originalTx; + return originalTx; + } catch (_) { + return {}; + } } Future broadcastTransaction( @@ -228,7 +258,7 @@ class ElectrumClient { if (result is String) { return result; } - + print(result); return ''; }); @@ -256,11 +286,13 @@ class ElectrumClient { return 0; }); - BehaviorSubject scripthashUpdate(String scripthash) => - subscribe( - id: 'blockchain.scripthash.subscribe:$scripthash', - method: 'blockchain.scripthash.subscribe', - params: [scripthash]); + BehaviorSubject scripthashUpdate(String scripthash) { + _id += 1; + return subscribe( + id: 'blockchain.scripthash.subscribe:$scripthash', + method: 'blockchain.scripthash.subscribe', + params: [scripthash]); + } BehaviorSubject subscribe( {@required String id, @@ -273,7 +305,8 @@ class ElectrumClient { return subscription; } - Future call({String method, List params = const []}) { + Future call({String method, List params = const []}) async { + await Future.delayed(Duration(milliseconds: 100)); final completer = Completer(); _id += 1; final id = _id; @@ -307,6 +340,12 @@ class ElectrumClient { socket.write(jsonrpc(method: method, id: _id, params: params)); } + Future close() async { + _aliveTimer.cancel(); + await socket.close(); + onConnectionStatusChange = null; + } + void _regisryTask(int id, Completer completer) => _tasks[id.toString()] = SocketTask(completer: completer, isSubscription: false); @@ -351,6 +390,29 @@ class ElectrumClient { _isConnected = isConnected; } + + void _handleResponse(Map response) { + final method = response['method']; + final id = response['id'] as String; + final result = response['result']; + + if (method is String) { + _methodHandler(method: method, request: response); + return; + } + + _finish(id, result); + } +} + +// FIXME: move me +bool isJSONStringCorrect(String source) { + try { + json.decode(source); + return true; + } catch (_) { + return false; + } } class RequestFailedTimeoutException implements Exception { diff --git a/lib/bitcoin/pending_bitcoin_transaction.dart b/lib/bitcoin/pending_bitcoin_transaction.dart index d2c018a35..edd5a0450 100644 --- a/lib/bitcoin/pending_bitcoin_transaction.dart +++ b/lib/bitcoin/pending_bitcoin_transaction.dart @@ -16,6 +16,7 @@ class PendingBitcoinTransaction with PendingTransaction { final int amount; final int fee; + @override String get id => _tx.getId(); @override diff --git a/lib/core/backup.dart b/lib/core/backup.dart index d4626fc02..760c7bbc0 100644 --- a/lib/core/backup.dart +++ b/lib/core/backup.dart @@ -205,8 +205,8 @@ class BackupService { _sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey), PreferencesKey.shouldSaveRecipientAddressKey: _sharedPreferences .getBool(PreferencesKey.shouldSaveRecipientAddressKey), - PreferencesKey.currentDarkTheme: - _sharedPreferences.getBool(PreferencesKey.currentDarkTheme), + PreferencesKey.isDarkThemeLegacy: + _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy), PreferencesKey.currentPinLength: _sharedPreferences.getInt(PreferencesKey.currentPinLength), PreferencesKey.currentTransactionPriorityKey: _sharedPreferences @@ -219,7 +219,7 @@ class BackupService { _sharedPreferences.getString(PreferencesKey.currentLanguageCode), PreferencesKey.displayActionListModeKey: _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey), - 'currentTheme': _sharedPreferences.getInt('current_theme') + PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme) // FIX-ME: Unnamed constant. }; diff --git a/lib/core/seed_validator.dart b/lib/core/seed_validator.dart index 36a06cb03..94ada7481 100644 --- a/lib/core/seed_validator.dart +++ b/lib/core/seed_validator.dart @@ -1,4 +1,4 @@ -import 'package:bip39/src/wordlists/english.dart' as bitcoin_english; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart' as bitcoin_electrum; import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/entities/mnemonic_item.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; @@ -64,7 +64,7 @@ class SeedValidator extends Validator { static List getBitcoinWordList(String language) { assert(language.toLowerCase() == LanguageList.english.toLowerCase()); - return bitcoin_english.WORDLIST; + return bitcoin_electrum.englishWordlist; } @override diff --git a/lib/core/wallet_base.dart b/lib/core/wallet_base.dart index 8eea39dca..fe187b4c7 100644 --- a/lib/core/wallet_base.dart +++ b/lib/core/wallet_base.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/balance.dart'; import 'package:flutter/foundation.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/core/pending_transaction.dart'; @@ -9,7 +10,7 @@ import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; -abstract class WalletBase { +abstract class WalletBase { WalletBase(this.walletInfo); static String idFor(String name, WalletType type) => @@ -52,4 +53,6 @@ abstract class WalletBase { Future save(); Future rescan({int height}); + + void close(); } diff --git a/lib/core/wallet_credentials.dart b/lib/core/wallet_credentials.dart index 3998bb005..ddfafab1a 100644 --- a/lib/core/wallet_credentials.dart +++ b/lib/core/wallet_credentials.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/entities/wallet_info.dart'; abstract class WalletCredentials { - WalletCredentials({this.name, this.password, this.height}); + WalletCredentials({this.name, this.password, this.height, this.walletInfo}); final String name; final int height; diff --git a/lib/di.dart b/lib/di.dart index f6cfd0aa6..eddffc232 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -19,6 +19,7 @@ import 'package:cake_wallet/src/screens/contact/contact_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart'; import 'package:cake_wallet/src/screens/faq/faq_page.dart'; +import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart'; import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; @@ -32,6 +33,7 @@ import 'package:cake_wallet/src/screens/send/send_template_page.dart'; import 'package:cake_wallet/src/screens/settings/change_language.dart'; import 'package:cake_wallet/src/screens/settings/settings.dart'; import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; +import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; @@ -63,6 +65,8 @@ import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model. import 'package:cake_wallet/view_model/rescan_view_model.dart'; import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart'; import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart'; +import 'package:cake_wallet/view_model/transaction_details_view_model.dart'; +import 'package:cake_wallet/view_model/trade_details_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/auth_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -340,7 +344,8 @@ Future setup( (ContactRecord contact, _) => ContactViewModel(contactSource, contact: contact)); - getIt.registerFactory(() => ContactListViewModel(contactSource)); + getIt.registerFactory( + () => ContactListViewModel(contactSource, walletInfoSource)); getIt.registerFactoryParam( (bool isEditable, _) => ContactListPage(getIt.get(), @@ -368,7 +373,8 @@ Future setup( getIt.get().wallet, tradesSource, getIt.get(), - getIt.get())); + getIt.get(), + getIt.get().settingsStore)); getIt.registerFactory(() => ExchangeTradeViewModel( wallet: getIt.get().wallet, @@ -389,7 +395,7 @@ Future setup( getIt.registerFactory(() => MoneroWalletService(walletInfoSource)); - getIt.registerFactory(() => BitcoinWalletService()); + getIt.registerFactory(() => BitcoinWalletService(walletInfoSource)); getIt.registerFactoryParam( (WalletType param1, __) { @@ -428,13 +434,28 @@ Future setup( getIt.registerFactoryParam((type, _) => WalletRestorePage(getIt.get(param1: type))); + getIt + .registerFactoryParam( + (TransactionInfo transactionInfo, _) => TransactionDetailsViewModel( + transactionInfo: transactionInfo, + transactionDescriptionBox: transactionDescriptionBox, + settingsStore: getIt.get())); + getIt.registerFactoryParam( (TransactionInfo transactionInfo, _) => TransactionDetailsPage( - transactionInfo, - getIt.get().shouldSaveRecipientAddress, - transactionDescriptionBox)); + transactionDetailsViewModel: + getIt.get(param1: transactionInfo))); - getIt.registerFactory(() => PreSeedPage()); + getIt.registerFactoryParam( + (para1, param2) => NewWalletTypePage(getIt.get(), + onTypeSelected: para1, isNewWallet: param2)); + + getIt.registerFactoryParam( + (WalletType type, _) => PreSeedPage(type)); + + getIt.registerFactoryParam((trade, _) => + TradeDetailsViewModel(tradeForDetails: trade, trades: tradesSource)); getIt.registerFactory(() => BackupService( getIt.get(), @@ -463,4 +484,7 @@ Future setup( getIt.registerFactory( () => RestoreFromBackupPage(getIt.get())); + + getIt.registerFactoryParam((Trade trade, _) => + TradeDetailsPage(getIt.get(param1: trade))); } diff --git a/lib/entities/balance.dart b/lib/entities/balance.dart index e8564c5a2..cf98f9e0f 100644 --- a/lib/entities/balance.dart +++ b/lib/entities/balance.dart @@ -1,3 +1,11 @@ abstract class Balance { - const Balance(); + const Balance(this.available, this.additional); + + final int available; + + final int additional; + + String get formattedAvailableBalance; + + String get formattedAdditionalBalance; } diff --git a/lib/entities/balance_display_mode.dart b/lib/entities/balance_display_mode.dart index 57197e57b..8b11bf385 100644 --- a/lib/entities/balance_display_mode.dart +++ b/lib/entities/balance_display_mode.dart @@ -7,15 +7,16 @@ class BalanceDisplayMode extends EnumerableItem with Serializable { : super(title: title, raw: raw); static const all = [ - BalanceDisplayMode.fullBalance, - BalanceDisplayMode.availableBalance, - BalanceDisplayMode.hiddenBalance + BalanceDisplayMode.hiddenBalance, + BalanceDisplayMode.displayableBalance, ]; static const fullBalance = BalanceDisplayMode(raw: 0, title: 'Full Balance'); static const availableBalance = BalanceDisplayMode(raw: 1, title: 'Available Balance'); static const hiddenBalance = BalanceDisplayMode(raw: 2, title: 'Hidden Balance'); + static const displayableBalance = + BalanceDisplayMode(raw: 3, title: 'Displayable Balance'); static BalanceDisplayMode deserialize({int raw}) { switch (raw) { @@ -25,6 +26,8 @@ class BalanceDisplayMode extends EnumerableItem with Serializable { return availableBalance; case 2: return hiddenBalance; + case 3: + return displayableBalance; default: return null; } @@ -39,6 +42,8 @@ class BalanceDisplayMode extends EnumerableItem with Serializable { return S.current.xmr_available_balance; case BalanceDisplayMode.hiddenBalance: return S.current.xmr_hidden; + case BalanceDisplayMode.displayableBalance: + return S.current.displayable; default: return ''; } diff --git a/lib/entities/contact_base.dart b/lib/entities/contact_base.dart new file mode 100644 index 000000000..a80fd1c21 --- /dev/null +++ b/lib/entities/contact_base.dart @@ -0,0 +1,9 @@ +import 'package:cake_wallet/entities/crypto_currency.dart'; + +abstract class ContactBase { + String name; + + String address; + + CryptoCurrency type; +} \ No newline at end of file diff --git a/lib/entities/contact_record.dart b/lib/entities/contact_record.dart index c4f55cc5a..ff535ecb0 100644 --- a/lib/entities/contact_record.dart +++ b/lib/entities/contact_record.dart @@ -3,21 +3,27 @@ import 'package:mobx/mobx.dart'; import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/record.dart'; +import 'package:cake_wallet/entities/contact_base.dart'; part 'contact_record.g.dart'; class ContactRecord = ContactRecordBase with _$ContactRecord; -abstract class ContactRecordBase extends Record with Store { +abstract class ContactRecordBase extends Record + with Store + implements ContactBase { ContactRecordBase(Box source, Contact original) : super(source, original); + @override @observable String name; + @override @observable String address; + @override @observable CryptoCurrency type; diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 28fc3da90..4859ffd05 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -1,4 +1,8 @@ -import 'dart:io' show Platform; +import 'dart:io' show File, Platform; +import 'package:cake_wallet/core/key_service.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/entities/pathForWallet.dart'; +import 'package:cake_wallet/monero/monero_wallet_service.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -45,7 +49,7 @@ Future defaultSettingsMigration( FiatCurrency.usd.toString()); await sharedPreferences.setInt( PreferencesKey.currentTransactionPriorityKey, - TransactionPriority.standart.raw); + TransactionPriority.standard.raw); await sharedPreferences.setInt( PreferencesKey.currentBalanceDisplayModeKey, BalanceDisplayMode.availableBalance.raw); @@ -73,6 +77,14 @@ Future defaultSettingsMigration( sharedPreferences: sharedPreferences, nodes: nodes); break; + case 5: + await addAddressesForMoneroWallets(walletInfoSource); + break; + + case 6: + await updateDisplayModes(sharedPreferences); + break; + default: break; } @@ -120,7 +132,7 @@ Future changeMoneroCurrentNodeToDefault( } Node getBitcoinDefaultElectrumServer({@required Box nodes}) { - final uri = 'electrumx.cakewallet.com:50002'; + final uri = 'electrum.cakewallet.com:50002'; return nodes.values .firstWhere((Node node) => node.uri == uri, orElse: () => null) ?? @@ -189,3 +201,34 @@ Future addBitcoinElectrumServerList({@required Box nodes}) async { final serverList = await loadElectrumServerList(); await nodes.addAll(serverList); } + +Future addAddressesForMoneroWallets( + Box walletInfoSource) async { + final moneroWalletsInfo = + walletInfoSource.values.where((info) => info.type == WalletType.monero); + moneroWalletsInfo.forEach((info) async { + try { + final walletPath = + await pathForWallet(name: info.name, type: WalletType.monero); + final addressFilePath = '$walletPath.address.txt'; + final addressFile = File(addressFilePath); + + if (!addressFile.existsSync()) { + return; + } + + final addressText = await addressFile.readAsString(); + info.address = addressText; + await info.save(); + } catch (e) { + print(e.toString()); + } + }); +} + +Future updateDisplayModes(SharedPreferences sharedPreferences) async { + final currentBalanceDisplayMode = + sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey); + final balanceDisplayMode = currentBalanceDisplayMode < 2 ? 3 : 2; + await sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode); +} diff --git a/lib/entities/load_current_wallet.dart b/lib/entities/load_current_wallet.dart index ef690521f..88c236088 100644 --- a/lib/entities/load_current_wallet.dart +++ b/lib/entities/load_current_wallet.dart @@ -19,5 +19,5 @@ Future loadCurrentWallet() async { await getIt.get().getWalletPassword(walletName: name); final _service = getIt.get(param1: type); final wallet = await _service.openWallet(name, password); - appStore.wallet = wallet; + appStore.changeCurrentWallet(wallet); } diff --git a/lib/entities/node_list.dart b/lib/entities/node_list.dart index 470b9bc99..118394e6f 100644 --- a/lib/entities/node_list.dart +++ b/lib/entities/node_list.dart @@ -39,9 +39,9 @@ Future> loadElectrumServerList() async { Future resetToDefault(Box nodeSource) async { final moneroNodes = await loadDefaultNodes(); - // final bitcoinElectrumServerList = await loadElectrumServerList(); - // final nodes = moneroNodes + bitcoinElectrumServerList; + final bitcoinElectrumServerList = await loadElectrumServerList(); + final nodes = moneroNodes + bitcoinElectrumServerList; await nodeSource.clear(); - await nodeSource.addAll(moneroNodes); + await nodeSource.addAll(nodes); } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 53d59460a..c2d6427c9 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -9,7 +9,8 @@ class PreferencesKey { static const shouldSaveRecipientAddressKey = 'save_recipient_address'; static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication'; - static const currentDarkTheme = 'dark_theme'; + static const currentTheme = 'current_theme'; + static const isDarkThemeLegacy = 'dark_theme'; static const displayActionListModeKey = 'display_list_mode'; static const currentPinLength = 'current_pin_length'; static const currentLanguageCode = 'language_code'; diff --git a/lib/entities/transaction_description.dart b/lib/entities/transaction_description.dart index 3f817fe4f..65f9d4263 100644 --- a/lib/entities/transaction_description.dart +++ b/lib/entities/transaction_description.dart @@ -4,7 +4,7 @@ part 'transaction_description.g.dart'; @HiveType(typeId: 2) class TransactionDescription extends HiveObject { - TransactionDescription({this.id, this.recipientAddress}); + TransactionDescription({this.id, this.recipientAddress, this.transactionNote}); static const boxName = 'TransactionDescriptions'; static const boxKey = 'transactionDescriptionsBoxKey'; @@ -14,4 +14,9 @@ class TransactionDescription extends HiveObject { @HiveField(1) String recipientAddress; + + @HiveField(2) + String transactionNote; + + String get note => transactionNote ?? ''; } diff --git a/lib/entities/transaction_info.dart b/lib/entities/transaction_info.dart index c9ad89c71..986ab384d 100644 --- a/lib/entities/transaction_info.dart +++ b/lib/entities/transaction_info.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/utils/mobx.dart'; abstract class TransactionInfo extends Object with Keyable { String id; int amount; + int fee; TransactionDirection direction; bool isPending; DateTime date; @@ -11,6 +12,7 @@ abstract class TransactionInfo extends Object with Keyable { int confirmations; String amountFormatted(); String fiatAmount(); + String feeFormatted(); void changeFiatAmount(String amount); @override diff --git a/lib/entities/transaction_priority.dart b/lib/entities/transaction_priority.dart index 9bad84c9f..b7661dbdc 100644 --- a/lib/entities/transaction_priority.dart +++ b/lib/entities/transaction_priority.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/entities/enumerable_item.dart'; @@ -17,7 +18,23 @@ class TransactionPriority extends EnumerableItem with Serializable { static const medium = TransactionPriority(title: 'Medium', raw: 2); static const fast = TransactionPriority(title: 'Fast', raw: 3); static const fastest = TransactionPriority(title: 'Fastest', raw: 4); - static const standart = slow; + static const standard = slow; + + + static List forWalletType(WalletType type) { + switch (type) { + case WalletType.monero: + return TransactionPriority.all; + case WalletType.bitcoin: + return [ + TransactionPriority.slow, + TransactionPriority.regular, + TransactionPriority.fast + ]; + default: + return []; + } + } static TransactionPriority deserialize({int raw}) { switch (raw) { diff --git a/lib/entities/wallet_contact.dart b/lib/entities/wallet_contact.dart new file mode 100644 index 000000000..97edf2ac6 --- /dev/null +++ b/lib/entities/wallet_contact.dart @@ -0,0 +1,15 @@ +import 'package:cake_wallet/entities/contact_base.dart'; +import 'package:cake_wallet/entities/crypto_currency.dart'; + +class WalletContact implements ContactBase { + WalletContact(this.address, this.name, this.type); + + @override + String address; + + @override + String name; + + @override + CryptoCurrency type; +} diff --git a/lib/entities/wallet_info.dart b/lib/entities/wallet_info.dart index 97ae9f326..50c9bdcce 100644 --- a/lib/entities/wallet_info.dart +++ b/lib/entities/wallet_info.dart @@ -7,7 +7,7 @@ part 'wallet_info.g.dart'; @HiveType(typeId: 4) class WalletInfo extends HiveObject { WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight, - this.timestamp, this.dirPath, this.path); + this.timestamp, this.dirPath, this.path, this.address); factory WalletInfo.external( {@required String id, @@ -17,9 +17,10 @@ class WalletInfo extends HiveObject { @required int restoreHeight, @required DateTime date, @required String dirPath, - @required String path}) { + @required String path, + @required String address}) { return WalletInfo(id, name, type, isRecovery, restoreHeight, - date.millisecondsSinceEpoch ?? 0, dirPath, path); + date.millisecondsSinceEpoch ?? 0, dirPath, path, address); } static const boxName = 'WalletInfo'; @@ -48,5 +49,8 @@ class WalletInfo extends HiveObject { @HiveField(7) String path; + @HiveField(8) + String address; + DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp); } diff --git a/lib/entities/wallet_type.dart b/lib/entities/wallet_type.dart index f1a48ce52..3f4d5c884 100644 --- a/lib/entities/wallet_type.dart +++ b/lib/entities/wallet_type.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:hive/hive.dart'; part 'wallet_type.g.dart'; @@ -48,3 +49,25 @@ String walletTypeToString(WalletType type) { return ''; } } + +String walletTypeToDisplayName(WalletType type) { + switch (type) { + case WalletType.monero: + return 'Monero'; + case WalletType.bitcoin: + return 'Bitcoin (Electrum)'; + default: + return ''; + } +} + +CryptoCurrency walletTypeToCryptoCurrency(WalletType type) { + switch (type) { + case WalletType.monero: + return CryptoCurrency.xmr; + case WalletType.bitcoin: + return CryptoCurrency.btc; + default: + return null; + } +} diff --git a/lib/exchange/xmrto/xmrto_exchange_provider.dart b/lib/exchange/xmrto/xmrto_exchange_provider.dart index 26adaedef..cc63ff1a3 100644 --- a/lib/exchange/xmrto/xmrto_exchange_provider.dart +++ b/lib/exchange/xmrto/xmrto_exchange_provider.dart @@ -27,11 +27,14 @@ class XMRTOExchangeProvider extends ExchangeProvider { static const _orderParameterUriSuffix = '/order_parameter_query'; static const _orderStatusUriSuffix = '/order_status_query/'; static const _orderCreateUriSuffix = '/order_create/'; + static const _headers = { + 'Content-Type': 'application/json', + 'User-Agent': userAgent + }; static Future _checkIsAvailable() async { const url = originalApiUri + _orderParameterUriSuffix; - final response = - await get(url, headers: {'Content-Type': 'application/json'}); + final response = await get(url, headers: _headers); return !(response.statusCode == 403); } @@ -91,9 +94,8 @@ class XMRTOExchangeProvider extends ExchangeProvider { Future createTrade({TradeRequest request}) async { final _request = request as XMRTOTradeRequest; final url = originalApiUri + _orderCreateUriSuffix; - final _amount = _request.isBTCRequest - ? _request.receiveAmount - : _request.amount; + final _amount = + _request.isBTCRequest ? _request.receiveAmount : _request.amount; final _amountCurrency = _request.isBTCRequest ? _request.to.toString() @@ -112,8 +114,8 @@ class XMRTOExchangeProvider extends ExchangeProvider { 'amount_currency': _amountCurrency, 'btc_dest_address': _request.address }; - final response = await post(url, - headers: {'Content-Type': 'application/json'}, body: json.encode(body)); + final response = + await post(url, headers: _headers, body: json.encode(body)); if (response.statusCode != 201) { if (response.statusCode == 400) { @@ -141,13 +143,10 @@ class XMRTOExchangeProvider extends ExchangeProvider { @override Future findTradeById({@required String id}) async { - const headers = { - 'Content-Type': 'application/json', - 'User-Agent': userAgent - }; final url = originalApiUri + _orderStatusUriSuffix; final body = {'uuid': id}; - final response = await post(url, headers: headers, body: json.encode(body)); + final response = + await post(url, headers: _headers, body: json.encode(body)); if (response.statusCode != 200) { if (response.statusCode == 400) { @@ -210,8 +209,7 @@ class XMRTOExchangeProvider extends ExchangeProvider { Future _fetchRates() async { try { final url = originalApiUri + _orderParameterUriSuffix; - final response = - await get(url, headers: {'Content-Type': 'application/json'}); + final response = await get(url, headers: _headers); final responseJSON = json.decode(response.body) as Map; final price = double.parse(responseJSON['price'] as String); diff --git a/lib/generated/i18n.dart b/lib/generated/i18n.dart index fd0c0f70e..2d312f92a 100644 --- a/lib/generated/i18n.dart +++ b/lib/generated/i18n.dart @@ -44,6 +44,7 @@ class S implements WidgetsLocalizations { String get authentication => "Authentication"; String get available_balance => "Available Balance"; String get biometric_auth_reason => "Scan your fingerprint to authenticate"; + String get bright_theme => "Bright"; String get buy => "Buy"; String get cake_wallet => "Cake Wallet"; String get cancel => "Cancel"; @@ -57,10 +58,12 @@ class S implements WidgetsLocalizations { String get choose_account => "Choose account"; String get choose_wallet_currency => "Please choose wallet currency:"; String get clear => "Clear"; + String get color_theme => "Color theme"; String get confirm => "Confirm"; String get confirm_delete_template => "This action will delete this template. Do you wish to continue?"; String get confirm_delete_wallet => "This action will delete this wallet. Do you wish to continue?"; String get confirm_sending => "Confirm sending"; + String get confirmations => "Confirmations"; String get contact => "Contact"; String get contact_name => "Contact Name"; String get continue_text => "Continue"; @@ -71,9 +74,11 @@ class S implements WidgetsLocalizations { String get create_new => "Create New Wallet"; String get create_new_account => "Create new account"; String get creating_new_wallet => "Creating new wallet"; + String get dark_theme => "Dark"; String get delete => "Delete"; String get digit_pin => "-digit PIN"; String get edit => "Edit"; + String get enter_your_note => "Enter your note…"; String get enter_your_pin => "Enter your PIN"; String get enter_your_pin_again => "Enter your pin again"; String get error => "Error"; @@ -101,12 +106,13 @@ class S implements WidgetsLocalizations { String get faq => "FAQ"; String get fetching => "Fetching"; String get filters => "Filter"; - String get first_wallet_text => "Awesome wallet for Monero"; + String get first_wallet_text => "Awesome wallet for Monero and Bitcoin"; String get full_balance => "Full Balance"; String get hidden_balance => "Hidden Balance"; String get id => "ID: "; String get incoming => "Incoming"; String get incorrect_seed => "The text entered is not valid."; + String get light_theme => "Light"; String get loading_your_wallet => "Loading your wallet"; String get login => "Login"; String get new_node_testing => "New node testing"; @@ -123,6 +129,8 @@ class S implements WidgetsLocalizations { String get node_test => "Test"; String get nodes => "Nodes"; String get nodes_list_reset_to_default_message => "Are you sure that you want to reset settings to default?"; + String get note_optional => "Note (optional)"; + String get note_tap_to_change => "Note (tap to change)"; String get offer_expires_in => "Offer expires in: "; String get ok => "OK"; String get openalias_alert_title => "XMR Recipient Detected"; @@ -135,17 +143,17 @@ class S implements WidgetsLocalizations { String get pin_is_incorrect => "PIN is incorrect"; String get placeholder_contacts => "Your contacts will be displayed here"; String get placeholder_transactions => "Your transactions will be displayed here"; - String get please_make_selection => "Please make selection below to create or recover your wallet."; + String get please_make_selection => "Please make a selection below to create or recover your wallet."; String get please_select => "Please select:"; String get please_try_to_connect_to_another_node => "Please try to connect to another node"; String get pre_seed_button_text => "I understand. Show me my seed"; - String get pre_seed_description => "On the next page you will see a series of 25 words. This is your unique and private seed and it is the ONLY way to recover your wallet in case of loss or malfunction. It is YOUR responsibility to write it down and store it in a safe place outside of the Cake Wallet app."; String get pre_seed_title => "IMPORTANT"; String get private_key => "Private key"; String get public_key => "Public key"; String get receive => "Receive"; String get receive_amount => "Amount"; String get received => "Received"; + String get recipient_address => "Recipient address"; String get reconnect => "Reconnect"; String get reconnect_alert_text => "Are you sure you want to reconnect?"; String get reconnection => "Reconnection"; @@ -214,7 +222,6 @@ class S implements WidgetsLocalizations { String get send_new => "New"; String get send_payment_id => "Payment ID (optional)"; String get send_sending => "Sending..."; - String get send_success => "Your Monero was successfully sent"; String get send_templates => "Templates"; String get send_title => "Send"; String get send_xmr => "Send XMR"; @@ -255,7 +262,7 @@ class S implements WidgetsLocalizations { String get subaddresses => "Subaddresses"; String get sync_status_connected => "CONNECTED"; String get sync_status_connecting => "CONNECTING"; - String get sync_status_failed_connect => "FAILED CONNECT TO THE NODE"; + String get sync_status_failed_connect => "DISCONNECTED"; String get sync_status_not_connected => "NOT CONNECTED"; String get sync_status_starting_sync => "STARTING SYNC"; String get sync_status_syncronized => "SYNCHRONIZED"; @@ -290,10 +297,12 @@ class S implements WidgetsLocalizations { String get trades => "Trades"; String get transaction_details_amount => "Amount"; String get transaction_details_date => "Date"; + String get transaction_details_fee => "Fee"; String get transaction_details_height => "Height"; String get transaction_details_recipient_address => "Recipient address"; String get transaction_details_title => "Transaction Details"; String get transaction_details_transaction_id => "Transaction ID"; + String get transaction_key => "Transaction Key"; String get transaction_priority_fast => "Fast"; String get transaction_priority_fastest => "Fastest"; String get transaction_priority_medium => "Medium"; @@ -348,10 +357,12 @@ class S implements WidgetsLocalizations { String min_value(String value, String currency) => "Min: ${value} ${currency}"; String openalias_alert_content(String recipient_name) => "You will be sending funds to\n${recipient_name}"; String powered_by(String title) => "Powered by ${title}"; + String pre_seed_description(String words) => "On the next page you will see a series of ${words} words. This is your unique and private seed and it is the ONLY way to recover your wallet in case of loss or malfunction. It is YOUR responsibility to write it down and store it in a safe place outside of the Cake Wallet app."; String provider_error(String provider) => "${provider} error"; String router_no_route(String name) => "No route defined for ${name}"; String send_address(String cryptoCurrency) => "${cryptoCurrency} address"; String send_priority(String transactionPriority) => "Currently the fee is set at ${transactionPriority} priority.\nTransaction priority can be adjusted in the settings"; + String send_success(String crypto) => "Your ${crypto} was successfully sent"; String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; String trade_details_copied(String title) => "${title} copied to Clipboard"; String trade_for_not_created(String title) => "Trade for ${title} is not created."; @@ -363,6 +374,10 @@ class S implements WidgetsLocalizations { String wallet_list_failed_to_remove(String wallet_name, String error) => "Failed to remove ${wallet_name} wallet. ${error}"; String wallet_list_loading_wallet(String wallet_name) => "Loading ${wallet_name} wallet"; String wallet_list_removing_wallet(String wallet_name) => "Removing ${wallet_name} wallet"; + String get exchange_incorrect_current_wallet_for_xmr => "If you want to exchange XMR from your Cake Wallet Monero balance, please switch to your Monero wallet first."; + String get confirmed => 'Confirmed'; + String get unconfirmed => 'Unconfirmed'; + String get displayable => 'Displayable'; } class $de extends S { @@ -394,6 +409,8 @@ class $de extends S { @override String get biometric_auth_reason => "Scannen Sie Ihren Fingerabdruck zur Authentifizierung"; @override + String get dark_theme => "Dunkel"; + @override String get transaction_sent => "Transaktion gesendet!"; @override String get send_fee => "Gebühr:"; @@ -440,6 +457,8 @@ class $de extends S { @override String get placeholder_contacts => "Ihre Kontakte werden hier angezeigt"; @override + String get transaction_key => "Transaktionsschlüssel"; + @override String get card_address => "Adresse:"; @override String get seed_language_portuguese => "Portugiesisch"; @@ -466,6 +485,8 @@ class $de extends S { @override String get send_your_wallet => "Deine Geldbörse"; @override + String get transaction_details_fee => "Gebühr"; + @override String get remove_node_message => "Möchten Sie den ausgewählten Knoten wirklich entfernen?"; @override String get error_text_account_name => "Der Kontoname darf nur Wallet und Zahlen enthalten\nund muss zwischen 1 und 15 Zeichen lang sein"; @@ -508,10 +529,10 @@ class $de extends S { @override String get choose_wallet_currency => "Bitte wählen Sie die Brieftaschenwährung:"; @override - String get pre_seed_description => "Auf der nächsten Seite sehen Sie eine Reihe von 25 Wörtern. Dies ist Ihr einzigartiger und privater Samen und der EINZIGE Weg, um Ihren Geldbeutel im Falle eines Verlusts oder einer Fehlfunktion wiederherzustellen. Es liegt in IHRER Verantwortung, es aufzuschreiben und an einem sicheren Ort außerhalb der Cake Wallet App aufzubewahren."; - @override String get node_connection_successful => "Die Verbindung war erfolgreich"; @override + String get confirmations => "Bestätigungen"; + @override String get confirm => "Bestätigen"; @override String get settings_display_balance_as => "Kontostand anzeigen als"; @@ -548,6 +569,8 @@ class $de extends S { @override String get address_book_menu => "Adressbuch"; @override + String get note_optional => "Hinweis (optional)"; + @override String get wallet_restoration_store_incorrect_seed_length => "Falsche Samenlänge"; @override String get seed_language_spanish => "Spanisch"; @@ -594,7 +617,7 @@ class $de extends S { @override String get exchange => "Austausch"; @override - String get sync_status_failed_connect => "Verbindung zum Knoten fehlgeschlagen"; + String get sync_status_failed_connect => "GETRENNT"; @override String get send_estimated_fee => "Geschätzte Gebühr:"; @override @@ -636,8 +659,6 @@ class $de extends S { @override String get trade_details_created_at => "Hergestellt in"; @override - String get send_success => "Ihr Monero wurde erfolgreich gesendet"; - @override String get settings_wallets => "Brieftaschen"; @override String get settings_only_transactions => "Nur Transaktionen"; @@ -646,6 +667,8 @@ class $de extends S { @override String get filters => "Filter"; @override + String get color_theme => "Farbthema"; + @override String get settings_current_node => "Aktueller Knoten"; @override String get copy_id => "ID kopieren"; @@ -676,6 +699,8 @@ class $de extends S { @override String get transaction_details_date => "Datum"; @override + String get note_tap_to_change => "Hinweis (zum Ändern tippen)"; + @override String get show_seed => "Seed zeigen"; @override String get send_error_currency => "Die Währung kann nur Zahlen enthalten"; @@ -756,6 +781,8 @@ class $de extends S { @override String get template => "Vorlage"; @override + String get enter_your_note => "Geben Sie Ihre Notiz ein…"; + @override String get transaction_priority_medium => "Mittel"; @override String get transaction_details_transaction_id => "Transaktions-ID"; @@ -886,12 +913,16 @@ class $de extends S { @override String get trade_state_confirming => "Bestätigung"; @override + String get bright_theme => "Hell"; + @override String get send => "Senden"; @override String get send_title => "Senden Sie"; @override String get error_text_keys => "Walletschlüssel können nur 64 hexadezimale Zeichen enthalten"; @override + String get light_theme => "Licht"; + @override String get settings_save_recipient_address => "Empfängeradresse speichern"; @override String get change_exchange_provider => "Wechseln Sie den Exchange-Anbieter"; @@ -964,6 +995,8 @@ class $de extends S { @override String get trade_state_btc_sent => "geschickt"; @override + String get recipient_address => "Empfängeradresse"; + @override String get address_book => "Adressbuch"; @override String get enter_your_pin => "PIN eingeben"; @@ -986,7 +1019,7 @@ class $de extends S { @override String get digit_pin => "-stelliger PIN"; @override - String get first_wallet_text => "tolle Brieftasche zum Monero"; + String get first_wallet_text => "tolle Brieftasche zum Monero und Bitcoin"; @override String get settings_trades => "Handel"; @override @@ -1004,6 +1037,8 @@ class $de extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "Handel für ${provider} wird nicht erstellt. Menge ist weniger als minimal: ${min} ${currency}"; @override + String pre_seed_description(String words) => "Auf der nächsten Seite sehen Sie eine Reihe von ${words} Wörtern. Dies ist Ihr einzigartiger und privater Samen und der EINZIGE Weg, um Ihren Geldbeutel im Falle eines Verlusts oder einer Fehlfunktion wiederherzustellen. Es liegt in IHRER Verantwortung, es aufzuschreiben und an einem sicheren Ort außerhalb der Cake Wallet App aufzubewahren."; + @override String trade_id_not_found(String tradeId, String title) => "Handel ${tradeId} von ${title} nicht gefunden."; @override String transaction_details_copied(String title) => "${title} in die Zwischenablage kopiert"; @@ -1020,6 +1055,8 @@ class $de extends S { @override String change_wallet_alert_content(String wallet_name) => "Möchten Sie die aktuelle Brieftasche in ändern ${wallet_name}?"; @override + String send_success(String crypto) => "Ihr ${crypto} wurde erfolgreich gesendet"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @@ -1055,6 +1092,14 @@ class $de extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "Laden fehlgeschlagen ${wallet_name} Wallet. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "Entfernen ${wallet_name} Wallet"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Wenn Sie XMR von Ihrem Cake Wallet Monero-Guthaben austauschen möchten, wechseln Sie bitte zuerst zu Ihrem Monero Wallet."; + @override + String get confirmed => 'Bestätigt'; + @override + String get unconfirmed => 'Unbestätigt'; + @override + String get displayable => 'Anzeigebar'; } class $hi extends S { @@ -1086,6 +1131,8 @@ class $hi extends S { @override String get biometric_auth_reason => "प्रमाणित करने के लिए अपने फ़िंगरप्रिंट को स्कैन करें"; @override + String get dark_theme => "अंधेरा"; + @override String get transaction_sent => "भेजा गया लेन-देन"; @override String get send_fee => "शुल्क:"; @@ -1132,6 +1179,8 @@ class $hi extends S { @override String get placeholder_contacts => "आपके संपर्क यहां प्रदर्शित होंगे"; @override + String get transaction_key => "लेन-देन की"; + @override String get card_address => "पता:"; @override String get seed_language_portuguese => "पुर्तगाली"; @@ -1158,6 +1207,8 @@ class $hi extends S { @override String get send_your_wallet => "आपका बटुआ"; @override + String get transaction_details_fee => "शुल्क"; + @override String get remove_node_message => "क्या आप वाकई चयनित नोड को निकालना चाहते हैं?"; @override String get error_text_account_name => "खाता नाम में केवल अक्षर, संख्याएं हो सकती हैं\nऔर 1 और 15 वर्णों के बीच लंबा होना चाहिए"; @@ -1200,10 +1251,10 @@ class $hi extends S { @override String get choose_wallet_currency => "कृपया बटुआ मुद्रा चुनें:"; @override - String get pre_seed_description => "अगले पेज पर आपको 25 शब्दों की एक श्रृंखला दिखाई देगी। यह आपका अद्वितीय और निजी बीज है और नुकसान या खराबी के मामले में अपने बटुए को पुनर्प्राप्त करने का एकमात्र तरीका है। यह आपकी जिम्मेदारी है कि इसे नीचे लिखें और इसे Cake Wallet ऐप के बाहर सुरक्षित स्थान पर संग्रहीत करें।"; - @override String get node_connection_successful => "कनेक्शन सफल रहा"; @override + String get confirmations => "पुष्टिकरण"; + @override String get confirm => "की पुष्टि करें"; @override String get settings_display_balance_as => "के रूप में संतुलन प्रदर्शित करें"; @@ -1240,6 +1291,8 @@ class $hi extends S { @override String get address_book_menu => "पता पुस्तिका"; @override + String get note_optional => "नोट (वैकल्पिक)"; + @override String get wallet_restoration_store_incorrect_seed_length => "गलत बीज की लंबाई"; @override String get seed_language_spanish => "स्पेनिश"; @@ -1286,7 +1339,7 @@ class $hi extends S { @override String get exchange => "अदला बदली"; @override - String get sync_status_failed_connect => "फेल हुआ कनेक्ट नोड"; + String get sync_status_failed_connect => "डिस्कनेक्ट किया गया"; @override String get send_estimated_fee => "अनुमानित शुल्क:"; @override @@ -1328,8 +1381,6 @@ class $hi extends S { @override String get trade_details_created_at => "पर बनाया गया"; @override - String get send_success => "आपका Monero सफलतापूर्वक भेजा गया"; - @override String get settings_wallets => "पर्स"; @override String get settings_only_transactions => "केवल लेन-देन"; @@ -1338,6 +1389,8 @@ class $hi extends S { @override String get filters => "फ़िल्टर"; @override + String get color_theme => "रंग विषय"; + @override String get settings_current_node => "वर्तमान नोड"; @override String get copy_id => "प्रतिलिपि ID"; @@ -1368,6 +1421,8 @@ class $hi extends S { @override String get transaction_details_date => "तारीख"; @override + String get note_tap_to_change => "नोट (टैप टू चेंज)"; + @override String get show_seed => "बीज दिखाओ"; @override String get send_error_currency => "मुद्रा में केवल संख्याएँ हो सकती हैं"; @@ -1448,6 +1503,8 @@ class $hi extends S { @override String get template => "खाका"; @override + String get enter_your_note => "अपना नोट दर्ज करें ..."; + @override String get transaction_priority_medium => "मध्यम"; @override String get transaction_details_transaction_id => "लेनदेन आईडी"; @@ -1578,12 +1635,16 @@ class $hi extends S { @override String get trade_state_confirming => "पुष्टि"; @override + String get bright_theme => "उज्ज्वल"; + @override String get send => "संदेश"; @override String get send_title => "संदेश"; @override String get error_text_keys => "वॉलेट कीज़ में हेक्स में केवल 64 वर्ण हो सकते हैं"; @override + String get light_theme => "रोशनी"; + @override String get settings_save_recipient_address => "प्राप्तकर्ता का पता सहेजें"; @override String get change_exchange_provider => "एक्सचेंज प्रदाता बदलें"; @@ -1656,6 +1717,8 @@ class $hi extends S { @override String get trade_state_btc_sent => "भेज दिया"; @override + String get recipient_address => "प्राप्तकर्ता का पता"; + @override String get address_book => "पता पुस्तिका"; @override String get enter_your_pin => "अपना पिन दर्ज करो"; @@ -1678,7 +1741,7 @@ class $hi extends S { @override String get digit_pin => "-अंक पिन"; @override - String get first_wallet_text => "बहुत बढ़िया बटुआ के लिये Monero"; + String get first_wallet_text => "Monero और Bitcoin के लिए बहुत बढ़िया बटुआ"; @override String get settings_trades => "ट्रेडों"; @override @@ -1696,6 +1759,8 @@ class $hi extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "व्यापार ${provider} के लिए नहीं बनाया गया है। राशि कम है तो न्यूनतम: ${min} ${currency}"; @override + String pre_seed_description(String words) => "अगले पेज पर आपको ${words} शब्दों की एक श्रृंखला दिखाई देगी। यह आपका अद्वितीय और निजी बीज है और नुकसान या खराबी के मामले में अपने बटुए को पुनर्प्राप्त करने का एकमात्र तरीका है। यह आपकी जिम्मेदारी है कि इसे नीचे लिखें और इसे Cake Wallet ऐप के बाहर सुरक्षित स्थान पर संग्रहीत करें।"; + @override String trade_id_not_found(String tradeId, String title) => "व्यापार ${tradeId} of ${title} नहीं मिला."; @override String transaction_details_copied(String title) => "${title} क्लिपबोर्ड पर नकल"; @@ -1712,6 +1777,8 @@ class $hi extends S { @override String change_wallet_alert_content(String wallet_name) => "क्या आप करंट वॉलेट को बदलना चाहते हैं ${wallet_name}?"; @override + String send_success(String crypto) => "आपका ${crypto} सफलतापूर्वक भेजा गया"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "मैक्स: ${value} ${currency}"; @@ -1747,6 +1814,14 @@ class $hi extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "लोड करने में विफल ${wallet_name} बटुआ. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "निकाला जा रहा है ${wallet_name} बटुआ"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "यदि आप अपने केक वॉलेट मोनेरो बैलेंस से एक्सएमआर का आदान-प्रदान करना चाहते हैं, तो कृपया अपने मोनेरो वॉलेट में जाएं।"; + @override + String get confirmed => 'की पुष्टि की'; + @override + String get unconfirmed => 'अपुष्ट'; + @override + String get displayable => 'प्रदर्शन योग्य'; } class $ru extends S { @@ -1778,6 +1853,8 @@ class $ru extends S { @override String get biometric_auth_reason => "Отсканируйте свой отпечаток пальца для аутентификации"; @override + String get dark_theme => "Темная"; + @override String get transaction_sent => "Tранзакция отправлена!"; @override String get send_fee => "Комиссия:"; @@ -1824,6 +1901,8 @@ class $ru extends S { @override String get placeholder_contacts => "Ваши контакты будут отображаться здесь"; @override + String get transaction_key => "Ключ транзакции"; + @override String get card_address => "Адрес:"; @override String get seed_language_portuguese => "Португальский"; @@ -1850,6 +1929,8 @@ class $ru extends S { @override String get send_your_wallet => "Ваш кошелёк"; @override + String get transaction_details_fee => "Комиссия"; + @override String get remove_node_message => "Вы уверены, что хотите удалить текущую ноду?"; @override String get error_text_account_name => "Имя аккаунта может содержать только буквы, цифры\nи должно быть от 1 до 15 символов в длину"; @@ -1892,10 +1973,10 @@ class $ru extends S { @override String get choose_wallet_currency => "Пожалуйста, выберите валюту кошелька:"; @override - String get pre_seed_description => "На следующей странице вы увидите серию из 25 слов. Это ваша уникальная и личная мнемоническая фраза, и это ЕДИНСТВЕННЫЙ способ восстановить свой кошелек в случае потери или неисправности. ВАМ необходимо записать ее и хранить в надежном месте вне приложения Cake Wallet."; - @override String get node_connection_successful => "Подключение прошло успешно"; @override + String get confirmations => "Подтверждения"; + @override String get confirm => "Подтвердить"; @override String get settings_display_balance_as => "Отображать баланс как"; @@ -1932,6 +2013,8 @@ class $ru extends S { @override String get address_book_menu => "Адресная книга"; @override + String get note_optional => "Примечание (необязательно)"; + @override String get wallet_restoration_store_incorrect_seed_length => "Неверная длина мнемонической фразы"; @override String get seed_language_spanish => "Испанский"; @@ -1978,7 +2061,7 @@ class $ru extends S { @override String get exchange => "Обмен"; @override - String get sync_status_failed_connect => "ОШИБКА ПОДКЛЮЧЕНИЯ К НОДЕ"; + String get sync_status_failed_connect => "ОТКЛЮЧЕНО"; @override String get send_estimated_fee => "Предполагаемая комиссия:"; @override @@ -2020,8 +2103,6 @@ class $ru extends S { @override String get trade_details_created_at => "Создано"; @override - String get send_success => "Ваш Monero был успешно отправлен"; - @override String get settings_wallets => "Кошельки"; @override String get settings_only_transactions => "Транзакции"; @@ -2030,6 +2111,8 @@ class $ru extends S { @override String get filters => "Фильтр"; @override + String get color_theme => "Цветовая тема"; + @override String get settings_current_node => "Текущая нода"; @override String get copy_id => "Скопировать ID"; @@ -2060,6 +2143,8 @@ class $ru extends S { @override String get transaction_details_date => "Дата"; @override + String get note_tap_to_change => "Примечание (нажмите для изменения)"; + @override String get show_seed => "Показать мнемоническую фразу"; @override String get send_error_currency => "Валюта может содержать только цифры"; @@ -2140,6 +2225,8 @@ class $ru extends S { @override String get template => "Шаблон"; @override + String get enter_your_note => "Введите примечание…"; + @override String get transaction_priority_medium => "Средний"; @override String get transaction_details_transaction_id => "ID транзакции"; @@ -2270,12 +2357,16 @@ class $ru extends S { @override String get trade_state_confirming => "Подтверждение"; @override + String get bright_theme => "Яркая"; + @override String get send => "Отправить"; @override String get send_title => "Отправить"; @override String get error_text_keys => "Ключи кошелька могут содержать только 64 символа в hex"; @override + String get light_theme => "Светлая"; + @override String get settings_save_recipient_address => "Сохранять адрес получателя"; @override String get change_exchange_provider => "Изменить провайдера обмена"; @@ -2348,6 +2439,8 @@ class $ru extends S { @override String get trade_state_btc_sent => "BTC отправлены"; @override + String get recipient_address => "Адрес получателя"; + @override String get address_book => "Адресная книга"; @override String get enter_your_pin => "Введите ваш PIN"; @@ -2370,7 +2463,7 @@ class $ru extends S { @override String get digit_pin => "-значный PIN"; @override - String get first_wallet_text => "В самом удобном кошельке для Monero"; + String get first_wallet_text => "В самом удобном кошельке для Monero и Bitcoin"; @override String get settings_trades => "Сделки"; @override @@ -2388,6 +2481,8 @@ class $ru extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "Сделка для ${provider} не создана. Сумма меньше минимальной: ${min} ${currency}"; @override + String pre_seed_description(String words) => "На следующей странице вы увидите серию из ${words} слов. Это ваша уникальная и личная мнемоническая фраза, и это ЕДИНСТВЕННЫЙ способ восстановить свой кошелек в случае потери или неисправности. ВАМ необходимо записать ее и хранить в надежном месте вне приложения Cake Wallet."; + @override String trade_id_not_found(String tradeId, String title) => "Сделка ${tradeId} ${title} не найдена."; @override String transaction_details_copied(String title) => "${title} скопировано в буфер обмена"; @@ -2404,6 +2499,8 @@ class $ru extends S { @override String change_wallet_alert_content(String wallet_name) => "Вы хотите изменить текущий кошелек на ${wallet_name}?"; @override + String send_success(String crypto) => "Ваш ${crypto} был успешно отправлен"; + @override String time(String minutes, String seconds) => "${minutes}мин ${seconds}сек"; @override String max_value(String value, String currency) => "Макс: ${value} ${currency}"; @@ -2439,6 +2536,14 @@ class $ru extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "Ошибка при загрузке ${wallet_name} кошелька. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "Удаление ${wallet_name} кошелька"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Если вы хотите обменять XMR со своего баланса Monero в Cake Wallet, сначала переключитесь на свой кошелек Monero."; + @override + String get confirmed => 'Подтверждено'; + @override + String get unconfirmed => 'Неподтвержденный'; + @override + String get displayable => 'Отображаемый'; } class $ko extends S { @@ -2470,6 +2575,8 @@ class $ko extends S { @override String get biometric_auth_reason => "지문을 스캔하여 인증"; @override + String get dark_theme => "어두운"; + @override String get transaction_sent => "거래가 전송되었습니다!"; @override String get send_fee => "회비:"; @@ -2516,6 +2623,8 @@ class $ko extends S { @override String get placeholder_contacts => "연락처가 여기에 표시됩니다"; @override + String get transaction_key => "거래 키"; + @override String get card_address => "주소:"; @override String get seed_language_portuguese => "포르투갈 인"; @@ -2542,6 +2651,8 @@ class $ko extends S { @override String get send_your_wallet => "지갑"; @override + String get transaction_details_fee => "회비"; + @override String get remove_node_message => "선택한 노드를 제거 하시겠습니까?"; @override String get error_text_account_name => "계정 이름은 문자, 숫자 만 포함 할 수 있습니다\n1 ~ 15 자 사이 여야합니다"; @@ -2584,10 +2695,10 @@ class $ko extends S { @override String get choose_wallet_currency => "지갑 통화를 선택하십시오:"; @override - String get pre_seed_description => "다음 페이지에서 25 개의 단어를 볼 수 있습니다. 이것은 귀하의 고유하고 개인적인 시드이며 분실 또는 오작동시 지갑을 복구하는 유일한 방법입니다. 기록해두고 Cake Wallet 앱 외부의 안전한 장소에 보관하는 것은 귀하의 책임입니다."; - @override String get node_connection_successful => "성공적으로 연결되었습니다."; @override + String get confirmations => "확인"; + @override String get confirm => "확인"; @override String get settings_display_balance_as => "잔액 표시"; @@ -2624,6 +2735,8 @@ class $ko extends S { @override String get address_book_menu => "주소록"; @override + String get note_optional => "참고 (선택 사항)"; + @override String get wallet_restoration_store_incorrect_seed_length => "시드 길이가 잘못되었습니다"; @override String get seed_language_spanish => "스페인의"; @@ -2670,7 +2783,7 @@ class $ko extends S { @override String get exchange => "교환"; @override - String get sync_status_failed_connect => "노드에 연결하지 못했습니다"; + String get sync_status_failed_connect => "연결 해제"; @override String get send_estimated_fee => "예상 수수료:"; @override @@ -2712,8 +2825,6 @@ class $ko extends S { @override String get trade_details_created_at => "에 작성"; @override - String get send_success => "Monero가 성공적으로 전송되었습니다"; - @override String get settings_wallets => "지갑"; @override String get settings_only_transactions => "거래 만"; @@ -2722,6 +2833,8 @@ class $ko extends S { @override String get filters => "필터"; @override + String get color_theme => "색상 테마"; + @override String get settings_current_node => "현재 노드"; @override String get copy_id => "부 ID"; @@ -2752,6 +2865,8 @@ class $ko extends S { @override String get transaction_details_date => "날짜"; @override + String get note_tap_to_change => "메모 (변경하려면 탭하세요)"; + @override String get show_seed => "종자 표시"; @override String get send_error_currency => "통화는 숫자 만 포함 할 수 있습니다"; @@ -2832,6 +2947,8 @@ class $ko extends S { @override String get template => "주형"; @override + String get enter_your_note => "메모를 입력하세요…"; + @override String get transaction_priority_medium => "매질"; @override String get transaction_details_transaction_id => "트랜잭션 ID"; @@ -2962,12 +3079,16 @@ class $ko extends S { @override String get trade_state_confirming => "확인 중"; @override + String get bright_theme => "선명한"; + @override String get send => "보내다"; @override String get send_title => "보내다"; @override String get error_text_keys => "지갑 키는 16 진수로 64 자만 포함 할 수 있습니다"; @override + String get light_theme => "빛"; + @override String get settings_save_recipient_address => "수신자 주소 저장"; @override String get change_exchange_provider => "교환 공급자 변경"; @@ -3040,6 +3161,8 @@ class $ko extends S { @override String get trade_state_btc_sent => "보냄"; @override + String get recipient_address => "받는 사람 주소"; + @override String get address_book => "주소록"; @override String get enter_your_pin => "PIN을 입력하십시오"; @@ -3062,7 +3185,7 @@ class $ko extends S { @override String get digit_pin => "숫자 PIN"; @override - String get first_wallet_text => "멋진 지갑 에 대한 Monero"; + String get first_wallet_text => "Monero 및 Bitcoin을위한 멋진 지갑"; @override String get settings_trades => "거래"; @override @@ -3080,6 +3203,8 @@ class $ko extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "거래 ${provider} 가 생성되지 않습니다. 금액이 최소보다 적습니다. ${min} ${currency}"; @override + String pre_seed_description(String words) => "다음 페이지에서 ${words} 개의 단어를 볼 수 있습니다. 이것은 귀하의 고유하고 개인적인 시드이며 분실 또는 오작동시 지갑을 복구하는 유일한 방법입니다. 기록해두고 Cake Wallet 앱 외부의 안전한 장소에 보관하는 것은 귀하의 책임입니다."; + @override String trade_id_not_found(String tradeId, String title) => "무역 ${tradeId} 의 ${title} 찾을 수 없습니다."; @override String transaction_details_copied(String title) => "${title} 클립 보드에 복사"; @@ -3096,6 +3221,8 @@ class $ko extends S { @override String change_wallet_alert_content(String wallet_name) => "현재 지갑을 다음으로 변경 하시겠습니까 ${wallet_name}?"; @override + String send_success(String crypto) => "${crypto}가 성공적으로 전송되었습니다"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "맥스: ${value} ${currency}"; @@ -3131,6 +3258,14 @@ class $ko extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "불러 오지 못했습니다 ${wallet_name} 지갑. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "풀이 ${wallet_name} 지갑"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Cake Wallet Monero 잔액에서 XMR을 교환하려면 먼저 Monero 지갑으로 전환하십시오."; + @override + String get confirmed => '확인'; + @override + String get unconfirmed => '미확인'; + @override + String get displayable => '표시 가능'; } class $pt extends S { @@ -3162,6 +3297,8 @@ class $pt extends S { @override String get biometric_auth_reason => "Digitalize sua impressão digital para autenticar"; @override + String get dark_theme => "Sombria"; + @override String get transaction_sent => "Transação enviada!"; @override String get send_fee => "Taxa:"; @@ -3208,6 +3345,8 @@ class $pt extends S { @override String get placeholder_contacts => "Seus contatos serão exibidos aqui"; @override + String get transaction_key => "Chave de transação"; + @override String get card_address => "Endereço:"; @override String get seed_language_portuguese => "Português"; @@ -3234,6 +3373,8 @@ class $pt extends S { @override String get send_your_wallet => "Sua carteira"; @override + String get transaction_details_fee => "Taxa"; + @override String get remove_node_message => "Você realmente deseja remover o nó selecionado?"; @override String get error_text_account_name => "O nome da conta só pode conter letras, números\ne deve ter entre 1 e 15 caracteres"; @@ -3276,10 +3417,10 @@ class $pt extends S { @override String get choose_wallet_currency => "Escolha a moeda da carteira:"; @override - String get pre_seed_description => "Na próxima página, você verá uma série de 25 palavras. Esta é a sua semente única e privada e é a ÚNICA maneira de recuperar sua carteira em caso de perda ou mau funcionamento. É SUA responsabilidade anotá-lo e armazená-lo em um local seguro fora do aplicativo Cake Wallet."; - @override String get node_connection_successful => "A conexão foi bem sucedida"; @override + String get confirmations => "Confirmações"; + @override String get confirm => "Confirmar"; @override String get settings_display_balance_as => "Saldo a exibir"; @@ -3316,6 +3457,8 @@ class $pt extends S { @override String get address_book_menu => "Livro de endereços"; @override + String get note_optional => "Nota (opcional)"; + @override String get wallet_restoration_store_incorrect_seed_length => "Comprimento de semente incorreto"; @override String get seed_language_spanish => "Espanhola"; @@ -3362,7 +3505,7 @@ class $pt extends S { @override String get exchange => "Trocar"; @override - String get sync_status_failed_connect => "Erro ao conectar com o nó"; + String get sync_status_failed_connect => "DESCONECTADO"; @override String get send_estimated_fee => "Taxa estimada:"; @override @@ -3404,8 +3547,6 @@ class $pt extends S { @override String get trade_details_created_at => "Criada em"; @override - String get send_success => "Seu Monero foi enviado com sucesso"; - @override String get settings_wallets => "Carteiras"; @override String get settings_only_transactions => "Somente transações"; @@ -3414,6 +3555,8 @@ class $pt extends S { @override String get filters => "Filtro"; @override + String get color_theme => "Tema de cor"; + @override String get settings_current_node => "Nó atual"; @override String get copy_id => "Copiar ID"; @@ -3444,6 +3587,8 @@ class $pt extends S { @override String get transaction_details_date => "Data"; @override + String get note_tap_to_change => "Nota (toque para alterar)"; + @override String get show_seed => "Mostrar semente"; @override String get send_error_currency => "A moeda só pode conter números"; @@ -3524,6 +3669,8 @@ class $pt extends S { @override String get template => "Modelo"; @override + String get enter_your_note => "Insira sua nota ..."; + @override String get transaction_priority_medium => "Média"; @override String get transaction_details_transaction_id => "ID da transação"; @@ -3654,12 +3801,16 @@ class $pt extends S { @override String get trade_state_confirming => "Confirmando"; @override + String get bright_theme => "Brilhante"; + @override String get send => "Enviar"; @override String get send_title => "Enviar"; @override String get error_text_keys => "As chaves da carteira podem conter apenas 64 caracteres em hexadecimal"; @override + String get light_theme => "Luz"; + @override String get settings_save_recipient_address => "Salvar endereço do destinatário"; @override String get change_exchange_provider => "Alterar o provedor de troca"; @@ -3732,6 +3883,8 @@ class $pt extends S { @override String get trade_state_btc_sent => "BTC enviado"; @override + String get recipient_address => "Endereço do destinatário"; + @override String get address_book => "Livro de endereços"; @override String get enter_your_pin => "Insira seu PIN"; @@ -3754,7 +3907,7 @@ class $pt extends S { @override String get digit_pin => "dígitos"; @override - String get first_wallet_text => "Uma fantástica carteira para Monero"; + String get first_wallet_text => "Uma fantástica carteira para Monero e Bitcoin"; @override String get settings_trades => "Trocas"; @override @@ -3772,6 +3925,8 @@ class $pt extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "A troca por ${provider} não é criada. O valor é menor que o mínimo: ${min} ${currency}"; @override + String pre_seed_description(String words) => "Na próxima página, você verá uma série de ${words} palavras. Esta é a sua semente única e privada e é a ÚNICA maneira de recuperar sua carteira em caso de perda ou mau funcionamento. É SUA responsabilidade anotá-lo e armazená-lo em um local seguro fora do aplicativo Cake Wallet."; + @override String trade_id_not_found(String tradeId, String title) => "A troca ${tradeId} de ${title} não foi encontrada."; @override String transaction_details_copied(String title) => "${title} copiados para a área de transferência"; @@ -3788,6 +3943,8 @@ class $pt extends S { @override String change_wallet_alert_content(String wallet_name) => "Quer mudar a carteira atual para ${wallet_name}?"; @override + String send_success(String crypto) => "Seu ${crypto} foi enviado com sucesso"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "Máx: ${value} ${currency}"; @@ -3823,6 +3980,14 @@ class $pt extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "Falha ao abrir a carteira ${wallet_name}. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "Removendo a carteira ${wallet_name}"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Se você deseja trocar o XMR de seu saldo da Carteira Monero Cake, troque primeiro para sua carteira Monero."; + @override + String get confirmed => 'Confirmada'; + @override + String get unconfirmed => 'Não confirmado'; + @override + String get displayable => 'Exibível'; } class $uk extends S { @@ -3854,6 +4019,8 @@ class $uk extends S { @override String get biometric_auth_reason => "Відскануйте свій відбиток пальця для аутентифікації"; @override + String get dark_theme => "Темна"; + @override String get transaction_sent => "Tранзакцію відправлено!"; @override String get send_fee => "Комісія:"; @@ -3900,6 +4067,8 @@ class $uk extends S { @override String get placeholder_contacts => "Тут будуть показані ваші контакти"; @override + String get transaction_key => "Ключ транзакції"; + @override String get card_address => "Адреса:"; @override String get seed_language_portuguese => "Португальська"; @@ -3926,6 +4095,8 @@ class $uk extends S { @override String get send_your_wallet => "Ваш гаманець"; @override + String get transaction_details_fee => "Комісія"; + @override String get remove_node_message => "Ви впевнені, що хочете видалити поточний вузол?"; @override String get error_text_account_name => "Ім'я акаунту може містити тільки букви, цифри\nі повинно бути від 1 до 15 символів в довжину"; @@ -3968,10 +4139,10 @@ class $uk extends S { @override String get choose_wallet_currency => "Будь ласка, виберіть валюту гаманця:"; @override - String get pre_seed_description => "На наступній сторінці ви побачите серію з 25 слів. Це ваша унікальна та приватна мнемонічна фраза, і це ЄДИНИЙ спосіб відновити ваш гаманець на випадок втрати або несправності. ВАМ необхідно записати її та зберігати в безпечному місці поза програмою Cake Wallet."; - @override String get node_connection_successful => "З'єднання було успішним"; @override + String get confirmations => "Підтвердження"; + @override String get confirm => "Підтвердити"; @override String get settings_display_balance_as => "Відображати баланс як"; @@ -4008,6 +4179,8 @@ class $uk extends S { @override String get address_book_menu => "Адресна книга"; @override + String get note_optional => "Примітка (необов’язково)"; + @override String get wallet_restoration_store_incorrect_seed_length => "Невірна довжина мнемонічної фрази"; @override String get seed_language_spanish => "Іспанська"; @@ -4054,7 +4227,7 @@ class $uk extends S { @override String get exchange => "Обмін"; @override - String get sync_status_failed_connect => "ПОМИЛКА ПІДКЛЮЧЕННЯ ДО ВУЗЛУ"; + String get sync_status_failed_connect => "ВІДКЛЮЧЕНО"; @override String get send_estimated_fee => "Ймовірна комісія:"; @override @@ -4096,8 +4269,6 @@ class $uk extends S { @override String get trade_details_created_at => "Створено"; @override - String get send_success => "Ваш Monero успішно надісланий"; - @override String get settings_wallets => "Гаманці"; @override String get settings_only_transactions => "Транзакції"; @@ -4106,6 +4277,8 @@ class $uk extends S { @override String get filters => "Фільтр"; @override + String get color_theme => "Кольорова тема"; + @override String get settings_current_node => "Поточний вузол"; @override String get copy_id => "Скопіювати ID"; @@ -4136,6 +4309,8 @@ class $uk extends S { @override String get transaction_details_date => "Дата"; @override + String get note_tap_to_change => "Примітка (натисніть для зміни)"; + @override String get show_seed => "Показати мнемонічну фразу"; @override String get send_error_currency => "Валюта може містити тільки цифри"; @@ -4216,6 +4391,8 @@ class $uk extends S { @override String get template => "Шаблон"; @override + String get enter_your_note => "Введіть примітку…"; + @override String get transaction_priority_medium => "Середній"; @override String get transaction_details_transaction_id => "ID транзакції"; @@ -4346,12 +4523,16 @@ class $uk extends S { @override String get trade_state_confirming => "Підтвердження"; @override + String get bright_theme => "Яскрава"; + @override String get send => "Відправити"; @override String get send_title => "Відправити"; @override String get error_text_keys => "Ключі гаманця можуть містити тільки 64 символів в hex"; @override + String get light_theme => "Світла"; + @override String get settings_save_recipient_address => "Зберігати адресу отримувача"; @override String get change_exchange_provider => "Змінити провайдера обміну"; @@ -4424,6 +4605,8 @@ class $uk extends S { @override String get trade_state_btc_sent => "BTC надіслано"; @override + String get recipient_address => "Адреса одержувача"; + @override String get address_book => "Адресна книга"; @override String get enter_your_pin => "Введіть ваш PIN"; @@ -4446,7 +4629,7 @@ class $uk extends S { @override String get digit_pin => "-значний PIN"; @override - String get first_wallet_text => "В самому зручному гаманці для Monero"; + String get first_wallet_text => "В самому зручному гаманці для Monero та Bitcoin"; @override String get settings_trades => "Операції"; @override @@ -4464,6 +4647,8 @@ class $uk extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "Операція для ${provider} не створена. Сума менша мінімальної: ${min} ${currency}"; @override + String pre_seed_description(String words) => "На наступній сторінці ви побачите серію з ${words} слів. Це ваша унікальна та приватна мнемонічна фраза, і це ЄДИНИЙ спосіб відновити ваш гаманець на випадок втрати або несправності. ВАМ необхідно записати її та зберігати в безпечному місці поза програмою Cake Wallet."; + @override String trade_id_not_found(String tradeId, String title) => "Операція ${tradeId} ${title} не знайдена."; @override String transaction_details_copied(String title) => "${title} скопійовано в буфер обміну"; @@ -4480,6 +4665,8 @@ class $uk extends S { @override String change_wallet_alert_content(String wallet_name) => "Ви хочете змінити поточний гаманець на ${wallet_name}?"; @override + String send_success(String crypto) => "Ваш ${crypto} успішно надісланий"; + @override String time(String minutes, String seconds) => "${minutes}хв ${seconds}сек"; @override String max_value(String value, String currency) => "Макс: ${value} ${currency}"; @@ -4515,6 +4702,14 @@ class $uk extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "Помилка при завантаженні ${wallet_name} гаманця. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "Видалення ${wallet_name} гаманця"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Якщо ви хочете обміняти XMR із вашого балансу Cake Wallet Monero, спочатку перейдіть на свій гаманець Monero."; + @override + String get confirmed => 'Підтверджено'; + @override + String get unconfirmed => 'Непідтверджений'; + @override + String get displayable => 'Відображуваний'; } class $ja extends S { @@ -4546,6 +4741,8 @@ class $ja extends S { @override String get biometric_auth_reason => "प指紋をスキャンして認証する"; @override + String get dark_theme => "闇"; + @override String get transaction_sent => "トランザクションが送信されました!"; @override String get send_fee => "費用:"; @@ -4592,6 +4789,8 @@ class $ja extends S { @override String get placeholder_contacts => "連絡先はここに表示されます"; @override + String get transaction_key => "トランザクションキー"; + @override String get card_address => "住所:"; @override String get seed_language_portuguese => "ポルトガル語"; @@ -4618,6 +4817,8 @@ class $ja extends S { @override String get send_your_wallet => "あなたの財布"; @override + String get transaction_details_fee => "費用"; + @override String get remove_node_message => "選択したノードを削除してもよろしいですか?"; @override String get error_text_account_name => "アカウント名には文字のみを含めることができます \n1〜15文字である必要があります"; @@ -4660,10 +4861,10 @@ class $ja extends S { @override String get choose_wallet_currency => "ウォレット通貨を選択してください:"; @override - String get pre_seed_description => "次のページでは、一連の25語が表示されます。 これはあなたのユニークでプライベートなシードであり、紛失や誤動作が発生した場合にウォレットを回復する唯一の方法です。 それを書き留めて、Cake Wallet アプリの外の安全な場所に保管するのはあなたの責任です。"; - @override String get node_connection_successful => "接続に成功しました"; @override + String get confirmations => "確認"; + @override String get confirm => "確認する"; @override String get settings_display_balance_as => "残高を表示"; @@ -4700,6 +4901,8 @@ class $ja extends S { @override String get address_book_menu => "住所録"; @override + String get note_optional => "注(オプション)"; + @override String get wallet_restoration_store_incorrect_seed_length => "誤ったシード長s"; @override String get seed_language_spanish => "スペイン語"; @@ -4746,7 +4949,7 @@ class $ja extends S { @override String get exchange => "交換する"; @override - String get sync_status_failed_connect => "ノードへの接続に失敗しました"; + String get sync_status_failed_connect => "切断されました"; @override String get send_estimated_fee => "見積手数料:"; @override @@ -4788,8 +4991,6 @@ class $ja extends S { @override String get trade_details_created_at => "で作成"; @override - String get send_success => "Moneroが送信されました"; - @override String get settings_wallets => "財布"; @override String get settings_only_transactions => "トランザクションのみ"; @@ -4798,6 +4999,8 @@ class $ja extends S { @override String get filters => "フィルタ"; @override + String get color_theme => "カラーテーマ"; + @override String get settings_current_node => "現在のノード"; @override String get copy_id => "IDをコピー"; @@ -4828,6 +5031,8 @@ class $ja extends S { @override String get transaction_details_date => "日付"; @override + String get note_tap_to_change => "注(タップして変更)"; + @override String get show_seed => "シードを表示"; @override String get send_error_currency => "通貨には数字のみを含めることができます"; @@ -4908,6 +5113,8 @@ class $ja extends S { @override String get template => "テンプレート"; @override + String get enter_your_note => "メモを入力してください…"; + @override String get transaction_priority_medium => "中"; @override String get transaction_details_transaction_id => "トランザクションID"; @@ -5038,12 +5245,16 @@ class $ja extends S { @override String get trade_state_confirming => "確認中"; @override + String get bright_theme => "明るい"; + @override String get send => "送る"; @override String get send_title => "を送信"; @override String get error_text_keys => "ウォレットキーには、16進数で64文字しか含めることができません"; @override + String get light_theme => "光"; + @override String get settings_save_recipient_address => "受信者のアドレスを保存"; @override String get change_exchange_provider => "Exchangeプロバイダーの変更"; @@ -5116,6 +5327,8 @@ class $ja extends S { @override String get trade_state_btc_sent => "送った"; @override + String get recipient_address => "受信者のアドレス"; + @override String get address_book => "住所録"; @override String get enter_your_pin => "PINを入力してください"; @@ -5138,7 +5351,7 @@ class $ja extends S { @override String get digit_pin => "桁ピン"; @override - String get first_wallet_text => "素晴らしい財布 ために Monero"; + String get first_wallet_text => "Moneroとビットコインのための素晴らしい財布"; @override String get settings_trades => "取引"; @override @@ -5156,6 +5369,8 @@ class $ja extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "${provider} の取引は作成されません。 金額は最小額より少ない: ${min} ${currency}"; @override + String pre_seed_description(String words) => "次のページでは、一連の${words}語が表示されます。 これはあなたのユニークでプライベートなシードであり、紛失や誤動作が発生した場合にウォレットを回復する唯一の方法です。 それを書き留めて、Cake Wallet アプリの外の安全な場所に保管するのはあなたの責任です。"; + @override String trade_id_not_found(String tradeId, String title) => "トレード ${tradeId} of ${title} 見つかりません"; @override String transaction_details_copied(String title) => "${title} クリップボードにコピーしました"; @@ -5172,6 +5387,8 @@ class $ja extends S { @override String change_wallet_alert_content(String wallet_name) => "現在のウォレットをに変更しますか ${wallet_name}?"; @override + String send_success(String crypto) => "${crypto}が送信されました"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "マックス: ${value} ${currency}"; @@ -5207,6 +5424,14 @@ class $ja extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "読み込みに失敗しました ${wallet_name} 財布. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "取りはずし ${wallet_name} 財布"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Cake Wallet Moneroの残高からXMRを交換する場合は、最初にMoneroウォレットに切り替えてください。"; + @override + String get confirmed => '確認済み'; + @override + String get unconfirmed => '未確認'; + @override + String get displayable => '表示可能'; } class $en extends S { @@ -5242,6 +5467,8 @@ class $pl extends S { @override String get biometric_auth_reason => "Zeskanuj swój odcisk palca, aby go uwierzytelnić"; @override + String get dark_theme => "Ciemny"; + @override String get transaction_sent => "Transakcja wysłana!"; @override String get send_fee => "Opłata:"; @@ -5288,6 +5515,8 @@ class $pl extends S { @override String get placeholder_contacts => "Twoje kontakty zostaną wyświetlone tutaj"; @override + String get transaction_key => "Klucz transakcji"; + @override String get card_address => "Adres:"; @override String get seed_language_portuguese => "Portugalski"; @@ -5314,6 +5543,8 @@ class $pl extends S { @override String get send_your_wallet => "Twój portfel"; @override + String get transaction_details_fee => "Opłata"; + @override String get remove_node_message => "Czy na pewno chcesz usunąć wybrany węzeł?"; @override String get error_text_account_name => "Nazwa konta może zawierać tylko litery, cyfry\ni musi mieć od 1 do 15 znaków"; @@ -5356,10 +5587,10 @@ class $pl extends S { @override String get choose_wallet_currency => "Wybierz walutę portfela:"; @override - String get pre_seed_description => "Na następnej stronie zobaczysz serię 25 słów. To jest Twoje unikalne i prywatne ziarno i jest to JEDYNY sposób na odzyskanie portfela w przypadku utraty lub awarii. Twoim obowiązkiem jest zapisanie go i przechowywanie w bezpiecznym miejscu poza aplikacją Cake Wallet."; - @override String get node_connection_successful => "Połączenie powiodło się"; @override + String get confirmations => "Potwierdzenia"; + @override String get confirm => "Potwierdzać"; @override String get settings_display_balance_as => "Wyświetl saldo jako"; @@ -5396,6 +5627,8 @@ class $pl extends S { @override String get address_book_menu => "Książka adresowa"; @override + String get note_optional => "Notatka (opcjonalnie)"; + @override String get wallet_restoration_store_incorrect_seed_length => "Nieprawidłowa długość nasion"; @override String get seed_language_spanish => "Hiszpański"; @@ -5442,7 +5675,7 @@ class $pl extends S { @override String get exchange => "Wymieniać się"; @override - String get sync_status_failed_connect => "NIE MOŻNA PODŁĄCZYĆ DO WĘZŁA"; + String get sync_status_failed_connect => "NIEPOWIĄZANY"; @override String get send_estimated_fee => "Szacowana opłata:"; @override @@ -5484,8 +5717,6 @@ class $pl extends S { @override String get trade_details_created_at => "Utworzono w"; @override - String get send_success => "Twoje Monero zostało pomyślnie wysłane"; - @override String get settings_wallets => "Portfele"; @override String get settings_only_transactions => "Tylko transakcje"; @@ -5494,6 +5725,8 @@ class $pl extends S { @override String get filters => "Filtr"; @override + String get color_theme => "Motyw kolorystyczny"; + @override String get settings_current_node => "Bieżący węzeł"; @override String get copy_id => "ID kopii"; @@ -5524,6 +5757,8 @@ class $pl extends S { @override String get transaction_details_date => "Data"; @override + String get note_tap_to_change => "Notatka (dotknij, aby zmienić)"; + @override String get show_seed => "Pokaż nasiona"; @override String get send_error_currency => "Waluta może zawierać tylko cyfry"; @@ -5604,6 +5839,8 @@ class $pl extends S { @override String get template => "Szablon"; @override + String get enter_your_note => "Wpisz notatkę…"; + @override String get transaction_priority_medium => "Średni"; @override String get transaction_details_transaction_id => "Transakcja ID"; @@ -5734,12 +5971,16 @@ class $pl extends S { @override String get trade_state_confirming => "Potwierdzam"; @override + String get bright_theme => "Jasny"; + @override String get send => "Wysłać"; @override String get send_title => "Wyślij"; @override String get error_text_keys => "Klucze portfela mogą zawierać tylko 64 znaki w systemie szesnastkowym"; @override + String get light_theme => "Lekki"; + @override String get settings_save_recipient_address => "Zapisz adres odbiorcy"; @override String get change_exchange_provider => "Zmień dostawcę programu Exchange"; @@ -5812,6 +6053,8 @@ class $pl extends S { @override String get trade_state_btc_sent => "Wysłane"; @override + String get recipient_address => "Adres odbiorcy"; + @override String get address_book => "Książka adresowa"; @override String get enter_your_pin => "Wpisz Twój kod PIN"; @@ -5834,7 +6077,7 @@ class $pl extends S { @override String get digit_pin => "-znak PIN"; @override - String get first_wallet_text => "Niesamowity portfel dla Monero"; + String get first_wallet_text => "Niesamowity portfel dla Monero i Bitcoin"; @override String get settings_trades => "Transakcje"; @override @@ -5852,6 +6095,8 @@ class $pl extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "Wymiana dla ${provider} nie została utworzona. Kwota jest mniejsza niż minimalna: ${min} ${currency}"; @override + String pre_seed_description(String words) => "Na następnej stronie zobaczysz serię ${words} słów. To jest Twoje unikalne i prywatne ziarno i jest to JEDYNY sposób na odzyskanie portfela w przypadku utraty lub awarii. Twoim obowiązkiem jest zapisanie go i przechowywanie w bezpiecznym miejscu poza aplikacją Cake Wallet."; + @override String trade_id_not_found(String tradeId, String title) => "Handel ${tradeId} of ${title} nie znaleziono."; @override String transaction_details_copied(String title) => "${title} skopiowane do schowka"; @@ -5868,6 +6113,8 @@ class $pl extends S { @override String change_wallet_alert_content(String wallet_name) => "Czy chcesz zmienić obecny portfel na ${wallet_name}?"; @override + String send_success(String crypto) => "Twoje ${crypto} zostało pomyślnie wysłane"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @@ -5903,6 +6150,14 @@ class $pl extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "Nie udało się załadować ${wallet_name} portfel. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "Usuwanie ${wallet_name} portfel"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Jeśli chcesz wymienić XMR z salda Cake Wallet Monero, najpierw przełącz się na portfel Monero."; + @override + String get confirmed => 'Potwierdzony'; + @override + String get unconfirmed => 'niepotwierdzony'; + @override + String get displayable => 'Wyświetlane'; } class $es extends S { @@ -5934,6 +6189,8 @@ class $es extends S { @override String get biometric_auth_reason => "Escanee su huella digital para autenticar"; @override + String get dark_theme => "Oscura"; + @override String get transaction_sent => "Transacción enviada!"; @override String get send_fee => "Cuota:"; @@ -5980,6 +6237,8 @@ class $es extends S { @override String get placeholder_contacts => "Tus contactos se mostrarán aquí"; @override + String get transaction_key => "Clave de transacción"; + @override String get card_address => "Dirección:"; @override String get seed_language_portuguese => "Portugués"; @@ -6006,6 +6265,8 @@ class $es extends S { @override String get send_your_wallet => "Tu billetera"; @override + String get transaction_details_fee => "Cuota"; + @override String get remove_node_message => "¿Está seguro de que desea eliminar el nodo seleccionado?"; @override String get error_text_account_name => "El nombre de la cuenta solo puede contener letras, números \ny debe tener entre 1 y 15 caracteres de longitud"; @@ -6048,10 +6309,10 @@ class $es extends S { @override String get choose_wallet_currency => "Por favor, elija la moneda de la billetera:"; @override - String get pre_seed_description => "En la página siguiente verá una serie de 25 palabras. Esta es su semilla única y privada y es la ÚNICA forma de recuperar su billetera en caso de pérdida o mal funcionamiento. Es SU responsabilidad escribirlo y guardarlo en un lugar seguro fuera de la aplicación Cake Wallet."; - @override String get node_connection_successful => "La conexión fue exitosa"; @override + String get confirmations => "Confirmaciones"; + @override String get confirm => "Confirmar"; @override String get settings_display_balance_as => "Mostrar saldo como"; @@ -6088,6 +6349,8 @@ class $es extends S { @override String get address_book_menu => "Libreta de direcciones"; @override + String get note_optional => "Nota (opcional)"; + @override String get wallet_restoration_store_incorrect_seed_length => "Longitud de semilla incorrecta"; @override String get seed_language_spanish => "Español"; @@ -6134,7 +6397,7 @@ class $es extends S { @override String get exchange => "Intercambiar"; @override - String get sync_status_failed_connect => "CONEXIÓN FALLIDA AL NODO"; + String get sync_status_failed_connect => "DESCONECTADO"; @override String get send_estimated_fee => "Tarifa estimada:"; @override @@ -6176,8 +6439,6 @@ class $es extends S { @override String get trade_details_created_at => "Creado en"; @override - String get send_success => "Su Monero fue enviado con éxito"; - @override String get settings_wallets => "Carteras"; @override String get settings_only_transactions => "Solo transacciones"; @@ -6186,6 +6447,8 @@ class $es extends S { @override String get filters => "Filtrar"; @override + String get color_theme => "Tema de color"; + @override String get settings_current_node => "Nodo actual"; @override String get copy_id => "Copiar ID"; @@ -6216,6 +6479,8 @@ class $es extends S { @override String get transaction_details_date => "Fecha"; @override + String get note_tap_to_change => "Nota (toque para cambiar)"; + @override String get show_seed => "Mostrar semilla"; @override String get send_error_currency => "La moneda solo puede contener números"; @@ -6296,6 +6561,8 @@ class $es extends S { @override String get template => "Plantilla"; @override + String get enter_your_note => "Ingresa tu nota…"; + @override String get transaction_priority_medium => "Medio"; @override String get transaction_details_transaction_id => "ID de transacción"; @@ -6426,12 +6693,16 @@ class $es extends S { @override String get trade_state_confirming => "Confirmando"; @override + String get bright_theme => "Brillante"; + @override String get send => "Enviar"; @override String get send_title => "Enviar"; @override String get error_text_keys => "Las llaves de billetera solo pueden contener 64 caracteres en hexadecimal"; @override + String get light_theme => "Ligera"; + @override String get settings_save_recipient_address => "Guardar dirección del destinatario"; @override String get change_exchange_provider => "Cambiar proveedor de intercambio"; @@ -6504,6 +6775,8 @@ class $es extends S { @override String get trade_state_btc_sent => "Btc expedido"; @override + String get recipient_address => "Dirección del receptor"; + @override String get address_book => "Libreta de direcciones"; @override String get enter_your_pin => "Introduce tu PIN"; @@ -6526,7 +6799,7 @@ class $es extends S { @override String get digit_pin => "-dígito PIN"; @override - String get first_wallet_text => "Impresionante billetera para Monero"; + String get first_wallet_text => "Impresionante billetera para Monero y Bitcoin"; @override String get settings_trades => "Comercia"; @override @@ -6544,6 +6817,8 @@ class $es extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "El comercio por ${provider} no se crea. La cantidad es menos que mínima: ${min} ${currency}"; @override + String pre_seed_description(String words) => "En la página siguiente verá una serie de ${words} palabras. Esta es su semilla única y privada y es la ÚNICA forma de recuperar su billetera en caso de pérdida o mal funcionamiento. Es SU responsabilidad escribirlo y guardarlo en un lugar seguro fuera de la aplicación Cake Wallet."; + @override String trade_id_not_found(String tradeId, String title) => "Comercio ${tradeId} de ${title} no encontrado."; @override String transaction_details_copied(String title) => "${title} Copiado al portapapeles"; @@ -6560,6 +6835,8 @@ class $es extends S { @override String change_wallet_alert_content(String wallet_name) => "¿Quieres cambiar la billetera actual a ${wallet_name}?"; @override + String send_success(String crypto) => "Su ${crypto} fue enviado con éxito"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @@ -6595,6 +6872,14 @@ class $es extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "No se pudo cargar ${wallet_name} la billetera. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "Retirar ${wallet_name} billetera"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Si desea intercambiar XMR de su saldo de Cake Wallet Monero, primero cambie a su billetera Monero."; + @override + String get confirmed => 'Confirmada'; + @override + String get unconfirmed => 'inconfirmado'; + @override + String get displayable => 'Visualizable'; } class $nl extends S { @@ -6626,6 +6911,8 @@ class $nl extends S { @override String get biometric_auth_reason => "Scan uw vingerafdruk om te verifiëren"; @override + String get dark_theme => "Donker"; + @override String get transaction_sent => "Transactie verzonden!"; @override String get send_fee => "Vergoeding:"; @@ -6672,6 +6959,8 @@ class $nl extends S { @override String get placeholder_contacts => "Je contacten worden hier weergegeven"; @override + String get transaction_key => "Transactiesleutel"; + @override String get card_address => "Adres:"; @override String get seed_language_portuguese => "Portugees"; @@ -6698,6 +6987,8 @@ class $nl extends S { @override String get send_your_wallet => "Uw portemonnee"; @override + String get transaction_details_fee => "Vergoeding"; + @override String get remove_node_message => "Weet u zeker dat u het geselecteerde knooppunt wilt verwijderen?"; @override String get error_text_account_name => "Accountnaam mag alleen letters, cijfers bevatten\nen moet tussen de 1 en 15 tekens lang zijn"; @@ -6740,10 +7031,10 @@ class $nl extends S { @override String get choose_wallet_currency => "Kies een portemonnee-valuta:"; @override - String get pre_seed_description => "Op de volgende pagina ziet u een reeks van 25 woorden. Dit is uw unieke en persoonlijke zaadje en het is de ENIGE manier om uw portemonnee te herstellen in geval van verlies of storing. Het is JOUW verantwoordelijkheid om het op te schrijven en op een veilige plaats op te slaan buiten de Cake Wallet app."; - @override String get node_connection_successful => "Verbinding is gelukt"; @override + String get confirmations => "Bevestigingen"; + @override String get confirm => "Bevestigen"; @override String get settings_display_balance_as => "Toon saldo als"; @@ -6780,6 +7071,8 @@ class $nl extends S { @override String get address_book_menu => "Adresboek"; @override + String get note_optional => "Opmerking (optioneel)"; + @override String get wallet_restoration_store_incorrect_seed_length => "Onjuiste zaadlengte"; @override String get seed_language_spanish => "Spaans"; @@ -6826,7 +7119,7 @@ class $nl extends S { @override String get exchange => "Uitwisseling"; @override - String get sync_status_failed_connect => "MISLUKT VERBINDING MET DE NODE"; + String get sync_status_failed_connect => "LOSGEKOPPELD"; @override String get send_estimated_fee => "Geschatte vergoeding:"; @override @@ -6868,8 +7161,6 @@ class $nl extends S { @override String get trade_details_created_at => "Gemaakt bij"; @override - String get send_success => "Uw Monero is succesvol verzonden"; - @override String get settings_wallets => "Portemonnee"; @override String get settings_only_transactions => "Alleen transacties"; @@ -6878,6 +7169,8 @@ class $nl extends S { @override String get filters => "Filter"; @override + String get color_theme => "Kleur thema"; + @override String get settings_current_node => "Huidige knooppunt"; @override String get copy_id => "ID kopiëren"; @@ -6908,6 +7201,8 @@ class $nl extends S { @override String get transaction_details_date => "Datum"; @override + String get note_tap_to_change => "Opmerking (tik om te wijzigen)"; + @override String get show_seed => "Toon zaad"; @override String get send_error_currency => "Valuta kan alleen cijfers bevatten"; @@ -6988,6 +7283,8 @@ class $nl extends S { @override String get template => "Sjabloon"; @override + String get enter_your_note => "Voer uw notitie in ..."; + @override String get transaction_priority_medium => "Medium"; @override String get transaction_details_transaction_id => "Transactie ID"; @@ -7118,12 +7415,16 @@ class $nl extends S { @override String get trade_state_confirming => "Bevestiging"; @override + String get bright_theme => "Helder"; + @override String get send => "Sturen"; @override String get send_title => "Stuur"; @override String get error_text_keys => "Portefeuillesleutels kunnen maximaal 64 tekens bevatten in hexadecimale volgorde"; @override + String get light_theme => "Licht"; + @override String get settings_save_recipient_address => "Adres ontvanger opslaan"; @override String get change_exchange_provider => "Wijzig Exchange Provider"; @@ -7196,6 +7497,8 @@ class $nl extends S { @override String get trade_state_btc_sent => "Verzonden"; @override + String get recipient_address => "Adres ontvanger"; + @override String get address_book => "Adresboek"; @override String get enter_your_pin => "Voer uw pincode in"; @@ -7218,7 +7521,7 @@ class $nl extends S { @override String get digit_pin => "-cijferige PIN"; @override - String get first_wallet_text => "Geweldige portemonnee fvoor Monero"; + String get first_wallet_text => "Geweldige portemonnee voor Monero en Bitcoin"; @override String get settings_trades => "Trades"; @override @@ -7236,6 +7539,8 @@ class $nl extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "Ruil voor ${provider} is niet gemaakt. Bedrag is minder dan minimaal: ${min} ${currency}"; @override + String pre_seed_description(String words) => "Op de volgende pagina ziet u een reeks van ${words} woorden. Dit is uw unieke en persoonlijke zaadje en het is de ENIGE manier om uw portemonnee te herstellen in geval van verlies of storing. Het is JOUW verantwoordelijkheid om het op te schrijven en op een veilige plaats op te slaan buiten de Cake Wallet app."; + @override String trade_id_not_found(String tradeId, String title) => "Handel ${tradeId} van ${title} niet gevonden."; @override String transaction_details_copied(String title) => "${title} gekopieerd naar het klembord"; @@ -7252,6 +7557,8 @@ class $nl extends S { @override String change_wallet_alert_content(String wallet_name) => "Wilt u de huidige portemonnee wijzigen in ${wallet_name}?"; @override + String send_success(String crypto) => "Uw ${crypto} is succesvol verzonden"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "Max: ${value} ${currency}"; @@ -7287,6 +7594,14 @@ class $nl extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "Laden mislukt ${wallet_name} portemonnee. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "Verwijderen ${wallet_name} portemonnee"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "Als u XMR wilt omwisselen van uw Cake Wallet Monero-saldo, moet u eerst overschakelen naar uw Monero-portemonnee."; + @override + String get confirmed => 'bevestigd'; + @override + String get unconfirmed => 'niet bevestigd'; + @override + String get displayable => 'Weer te geven'; } class $zh extends S { @@ -7318,6 +7633,8 @@ class $zh extends S { @override String get biometric_auth_reason => "掃描指紋以進行身份驗證"; @override + String get dark_theme => "黑暗"; + @override String get transaction_sent => "交易已发送"; @override String get send_fee => "費用:"; @@ -7364,6 +7681,8 @@ class $zh extends S { @override String get placeholder_contacts => "您的聯繫人將顯示在這裡"; @override + String get transaction_key => "交易密碼"; + @override String get card_address => "地址:"; @override String get seed_language_portuguese => "葡萄牙語"; @@ -7390,6 +7709,8 @@ class $zh extends S { @override String get send_your_wallet => "你的钱包"; @override + String get transaction_details_fee => "費用"; + @override String get remove_node_message => "您确定要删除所选节点吗?"; @override String get error_text_account_name => "帐户名称只能包含字母数字\n且必须介于1到15个字符之间"; @@ -7432,10 +7753,10 @@ class $zh extends S { @override String get choose_wallet_currency => "請選擇錢包貨幣:"; @override - String get pre_seed_description => "在下一頁上,您將看到一系列25個單詞。 這是您獨特的私人種子,是丟失或出現故障時恢復錢包的唯一方法。 您有責任將其寫下並存儲在Cake Wallet應用程序外部的安全地方。"; - @override String get node_connection_successful => "連接成功"; @override + String get confirmations => "確認書"; + @override String get confirm => "确认"; @override String get settings_display_balance_as => "将余额显示为"; @@ -7472,6 +7793,8 @@ class $zh extends S { @override String get address_book_menu => "地址簿"; @override + String get note_optional => "注意(可選)"; + @override String get wallet_restoration_store_incorrect_seed_length => "种子长度错误"; @override String get seed_language_spanish => "西班牙文"; @@ -7518,7 +7841,7 @@ class $zh extends S { @override String get exchange => "交换"; @override - String get sync_status_failed_connect => "无法连接到节点"; + String get sync_status_failed_connect => "斷線"; @override String get send_estimated_fee => "预估费用:"; @override @@ -7560,8 +7883,6 @@ class $zh extends S { @override String get trade_details_created_at => "创建于"; @override - String get send_success => "你Monero已成功發送"; - @override String get settings_wallets => "皮夹"; @override String get settings_only_transactions => "仅交易"; @@ -7570,6 +7891,8 @@ class $zh extends S { @override String get filters => "過濾"; @override + String get color_theme => "顏色主題"; + @override String get settings_current_node => "当前节点"; @override String get copy_id => "复印ID"; @@ -7600,6 +7923,8 @@ class $zh extends S { @override String get transaction_details_date => "日期"; @override + String get note_tap_to_change => "注意(輕按即可更改)"; + @override String get show_seed => "显示种子"; @override String get send_error_currency => "货币只能包含数字"; @@ -7680,6 +8005,8 @@ class $zh extends S { @override String get template => "模板"; @override + String get enter_your_note => "輸入您的筆記..."; + @override String get transaction_priority_medium => "介质"; @override String get transaction_details_transaction_id => "交易编号"; @@ -7810,12 +8137,16 @@ class $zh extends S { @override String get trade_state_confirming => "确认中"; @override + String get bright_theme => "亮"; + @override String get send => "发送"; @override String get send_title => "發送"; @override String get error_text_keys => "钱包密钥只能包含16个字符的十六进制字符"; @override + String get light_theme => "光"; + @override String get settings_save_recipient_address => "保存收件人地址"; @override String get change_exchange_provider => "更改交易所提供商"; @@ -7888,6 +8219,8 @@ class $zh extends S { @override String get trade_state_btc_sent => "已发送"; @override + String get recipient_address => "收件人地址"; + @override String get address_book => "地址簿"; @override String get enter_your_pin => "输入密码"; @@ -7910,7 +8243,7 @@ class $zh extends S { @override String get digit_pin => "数字别针"; @override - String get first_wallet_text => "很棒的钱包 对于 Monero"; + String get first_wallet_text => "很棒的Monero和比特幣錢包"; @override String get settings_trades => "交易"; @override @@ -7928,6 +8261,8 @@ class $zh extends S { @override String error_text_minimal_limit(String provider, String min, String currency) => "未創建 ${provider} 交易。 金額少於最小值:${min} ${currency}"; @override + String pre_seed_description(String words) => "在下一頁上,您將看到一系列${words}個單詞。 這是您獨特的私人種子,是丟失或出現故障時恢復錢包的唯一方法。 您有責任將其寫下並存儲在Cake Wallet應用程序外部的安全地方。"; + @override String trade_id_not_found(String tradeId, String title) => "贸易方式 ${tradeId} 的 ${title} 未找到."; @override String transaction_details_copied(String title) => "${title} 复制到剪贴板"; @@ -7944,6 +8279,8 @@ class $zh extends S { @override String change_wallet_alert_content(String wallet_name) => "您要將當前的錢包更改為 ${wallet_name}?"; @override + String send_success(String crypto) => "你${crypto}已成功發送"; + @override String time(String minutes, String seconds) => "${minutes}m ${seconds}s"; @override String max_value(String value, String currency) => "最高: ${value} ${currency}"; @@ -7979,6 +8316,14 @@ class $zh extends S { String wallet_list_failed_to_load(String wallet_name, String error) => "加载失败 ${wallet_name} 钱包. ${error}"; @override String wallet_list_removing_wallet(String wallet_name) => "拆下 ${wallet_name} 钱包"; + @override + String get exchange_incorrect_current_wallet_for_xmr => "如果要从Cake Wallet Monero余额中兑换XMR,请先切换到Monero钱包。"; + @override + String get confirmed => '已确认'; + @override + String get unconfirmed => '未经证实'; + @override + String get displayable => '可显示'; } class GeneratedLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/main.dart b/lib/main.dart index 7813bd375..0f8bd39e3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ import 'package:cake_wallet/core/backup.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; @@ -55,11 +57,11 @@ void main() async { TransactionDescription.boxName, encryptionKey: transactionDescriptionsBoxKey); final trades = - await Hive.openBox(Trade.boxName, encryptionKey: tradesBoxKey); + await Hive.openBox(Trade.boxName, encryptionKey: tradesBoxKey); final walletInfoSource = await Hive.openBox(WalletInfo.boxName); final templates = await Hive.openBox