diff --git a/.github/ISSUE_TEMPLATE/bug-report-🪲-.md b/.github/ISSUE_TEMPLATE/bug-report.md similarity index 100% rename from .github/ISSUE_TEMPLATE/bug-report-🪲-.md rename to .github/ISSUE_TEMPLATE/bug-report.md diff --git a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request-✨.md b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md similarity index 100% rename from .github/ISSUE_TEMPLATE/feature-or-enhancement-request-✨.md rename to .github/ISSUE_TEMPLATE/feature-or-enhancement-request.md diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 59b5162a2..b8eb2dd39 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -161,6 +161,7 @@ jobs: echo "const quantexExchangeMarkup = '${{ secrets.QUANTEX_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart echo "const nano2ApiKey = '${{ secrets.NANO2_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart + echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart - name: Rename app run: | diff --git a/assets/tron_node_list.yml b/assets/tron_node_list.yml index b12a82dbe..f9fd91179 100644 --- a/assets/tron_node_list.yml +++ b/assets/tron_node_list.yml @@ -4,5 +4,9 @@ useSSL: true - uri: api.trongrid.io + is_default: false + useSSL: true +- + uri: trx.nownodes.io is_default: true useSSL: true \ No newline at end of file diff --git a/cw_bitcoin/lib/bitcoin_mnemonic.dart b/cw_bitcoin/lib/bitcoin_mnemonic.dart index 4a01d6ddc..905aece28 100644 --- a/cw_bitcoin/lib/bitcoin_mnemonic.dart +++ b/cw_bitcoin/lib/bitcoin_mnemonic.dart @@ -2,9 +2,9 @@ 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; import 'package:cw_core/sec_random_native.dart'; +import 'package:cw_core/utils/text_normalizer.dart'; const segwit = '100'; final wordlist = englishWordlist; @@ -137,121 +137,6 @@ bool validateMnemonic(String mnemonic, {String prefix = segwit}) { } } -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', @@ -2301,4 +2186,4 @@ final englishWordlist = [ 'zero', 'zone', 'zoo' -]; \ No newline at end of file +]; diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 3954631e8..d061480ed 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -6,6 +6,7 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:convert/convert.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; +import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; @@ -150,7 +151,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ); // set the default if not present: - walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? "m/0'/0"; + walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? electrum_path; walletInfo.derivationInfo!.derivationType = snp.derivationType ?? DerivationType.electrum; Uint8List? seedBytes = null; diff --git a/cw_bitcoin/lib/electrum_derivations.dart b/cw_bitcoin/lib/electrum_derivations.dart index 7e19f1cb4..749e5c7af 100644 --- a/cw_bitcoin/lib/electrum_derivations.dart +++ b/cw_bitcoin/lib/electrum_derivations.dart @@ -108,3 +108,6 @@ Map> electrum_derivations = { ), ], }; + + +String electrum_path = electrum_derivations[DerivationType.electrum]!.first.derivationPath!; \ No newline at end of file diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index d3736e076..db42e2356 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -17,6 +17,7 @@ import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/bitcoin_wallet_keys.dart'; import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; +import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_bitcoin/electrum_transaction_history.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; @@ -133,7 +134,7 @@ abstract class ElectrumWalletBase return currency == CryptoCurrency.bch ? bitcoinCashHDWallet(seedBytes) : bitcoin.HDWallet.fromSeed(seedBytes, network: networkType) - .derivePath(_hardenedDerivationPath(derivationInfo?.derivationPath ?? "m/0'")); + .derivePath(_hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path)); } return bitcoin.HDWallet.fromBase58(xpub!); diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 3e3f39131..15ad1cf63 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; +import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/utils/file.dart'; @@ -71,7 +72,7 @@ class ElectrumWalletSnapshot { final derivationType = DerivationType .values[(data['derivationTypeIndex'] as int?) ?? DerivationType.electrum.index]; - final derivationPath = data['derivationPath'] as String? ?? "m/0'/0"; + final derivationPath = data['derivationPath'] as String? ?? electrum_path; try { regularAddressIndexByType = { diff --git a/cw_bitcoin/lib/utils.dart b/cw_bitcoin/lib/utils.dart index b3707e764..e3ebc00db 100644 --- a/cw_bitcoin/lib/utils.dart +++ b/cw_bitcoin/lib/utils.dart @@ -5,58 +5,64 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData; import 'package:hex/hex.dart'; -bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, int? index}) { - final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; +bitcoin.PaymentData generatePaymentData({ + required bitcoin.HDWallet hd, + required int index, +}) { + final pubKey = hd.derive(index).pubKey!; return PaymentData(pubkey: Uint8List.fromList(HEX.decode(pubKey))); } -ECPrivate generateECPrivate( - {required bitcoin.HDWallet hd, required BasedUtxoNetwork network, int? index}) { - final wif = index != null ? hd.derive(index).wif! : hd.wif!; +ECPrivate generateECPrivate({ + required bitcoin.HDWallet hd, + required BasedUtxoNetwork network, + required int index, +}) { + final wif = hd.derive(index).wif!; return ECPrivate.fromWif(wif, netVersion: network.wifNetVer); } String generateP2WPKHAddress({ required bitcoin.HDWallet hd, required BasedUtxoNetwork network, - int? index, + required int index, }) { - final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + final pubKey = hd.derive(index).pubKey!; return ECPublic.fromHex(pubKey).toP2wpkhAddress().toAddress(network); } String generateP2SHAddress({ required bitcoin.HDWallet hd, required BasedUtxoNetwork network, - int? index, + required int index, }) { - final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + final pubKey = hd.derive(index).pubKey!; return ECPublic.fromHex(pubKey).toP2wpkhInP2sh().toAddress(network); } String generateP2WSHAddress({ required bitcoin.HDWallet hd, required BasedUtxoNetwork network, - int? index, + required int index, }) { - final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + final pubKey = hd.derive(index).pubKey!; return ECPublic.fromHex(pubKey).toP2wshAddress().toAddress(network); } String generateP2PKHAddress({ required bitcoin.HDWallet hd, required BasedUtxoNetwork network, - int? index, + required int index, }) { - final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + final pubKey = hd.derive(index).pubKey!; return ECPublic.fromHex(pubKey).toP2pkhAddress().toAddress(network); } String generateP2TRAddress({ required bitcoin.HDWallet hd, required BasedUtxoNetwork network, - int? index, + required int index, }) { - final pubKey = index != null ? hd.derive(index).pubKey! : hd.pubKey!; + final pubKey = hd.derive(index).pubKey!; return ECPublic.fromHex(pubKey).toTaprootAddress().toAddress(network); } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 997ed9452..ffc224e93 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -863,7 +863,7 @@ packages: source: hosted version: "1.3.2" unorm_dart: - dependency: "direct main" + dependency: transitive description: name: unorm_dart sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b" diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index b0c1aa179..5eb91178d 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -28,7 +28,6 @@ dependencies: url: https://github.com/cake-tech/bitbox-flutter.git ref: Add-Support-For-OP-Return-data rxdart: ^0.27.5 - unorm_dart: ^0.2.0 cryptography: ^2.0.5 bitcoin_base: git: diff --git a/cw_core/lib/transaction_info.dart b/cw_core/lib/transaction_info.dart index 992582ff8..7b02bf1ff 100644 --- a/cw_core/lib/transaction_info.dart +++ b/cw_core/lib/transaction_info.dart @@ -16,6 +16,7 @@ abstract class TransactionInfo extends Object with Keyable { void changeFiatAmount(String amount); String? to; String? from; + String? evmSignatureName; List? inputAddresses; List? outputAddresses; diff --git a/cw_core/lib/utils/text_normalizer.dart b/cw_core/lib/utils/text_normalizer.dart new file mode 100644 index 000000000..5aeb5fd21 --- /dev/null +++ b/cw_core/lib/utils/text_normalizer.dart @@ -0,0 +1,117 @@ +import 'package:unorm_dart/unorm_dart.dart' as unorm; + +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 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(''); +} + +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(''); +} + +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; +} + +/// This method normalize text which transforms Unicode text into an equivalent decomposed form, allowing for easier sorting and searching of text. +String normalizeText(String source) { + final res = + _removeCombiningCharacters(unorm.nfkd(source).toLowerCase()).trim().split('/\s+/').join(' '); + + return _removeCJKSpaces(res); +} diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index abfdbfc58..88fddae09 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -656,6 +656,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + unorm_dart: + dependency: "direct main" + description: + name: unorm_dart + sha256: "5b35bff83fce4d76467641438f9e867dc9bcfdb8c1694854f230579d68cd8f4b" + url: "https://pub.dev" + source: hosted + version: "0.2.0" vector_math: dependency: transitive description: diff --git a/cw_core/pubspec.yaml b/cw_core/pubspec.yaml index 0a181a9c9..b2c16276d 100644 --- a/cw_core/pubspec.yaml +++ b/cw_core/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: intl: ^0.18.0 encrypt: ^5.0.1 socks5_proxy: ^1.0.4 + unorm_dart: ^0.3.0 # tor: # git: # url: https://github.com/cake-tech/tor.git diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart index f2b25bcdd..9d50fdd5b 100644 --- a/cw_ethereum/lib/ethereum_client.dart +++ b/cw_ethereum/lib/ethereum_client.dart @@ -29,6 +29,11 @@ class EthereumClient extends EVMChainClient { final jsonResponse = json.decode(response.body) as Map; + if (jsonResponse['result'] is String) { + log(jsonResponse['result']); + return []; + } + if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) { return (jsonResponse['result'] as List) .map((e) => EVMChainTransactionModel.fromJson(e as Map, 'ETH')) diff --git a/cw_ethereum/lib/ethereum_transaction_info.dart b/cw_ethereum/lib/ethereum_transaction_info.dart index d5d3fea8d..7a62c0b6a 100644 --- a/cw_ethereum/lib/ethereum_transaction_info.dart +++ b/cw_ethereum/lib/ethereum_transaction_info.dart @@ -14,6 +14,7 @@ class EthereumTransactionInfo extends EVMChainTransactionInfo { required super.confirmations, required super.to, required super.from, + super.evmSignatureName, super.exponent, }); @@ -31,6 +32,7 @@ class EthereumTransactionInfo extends EVMChainTransactionInfo { tokenSymbol: data['tokenSymbol'] as String, to: data['to'], from: data['from'], + evmSignatureName: data['evmSignatureName'], ); } diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index 4604db662..2c58cd31d 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -94,6 +94,7 @@ class EthereumWallet extends EVMChainWallet { tokenSymbol: transactionModel.tokenSymbol ?? "ETH", to: transactionModel.to, from: transactionModel.from, + evmSignatureName: transactionModel.evmSignatureName, ); return model; } diff --git a/cw_evm/lib/evm_chain_transaction_info.dart b/cw_evm/lib/evm_chain_transaction_info.dart index 329061db2..fb6cf6b52 100644 --- a/cw_evm/lib/evm_chain_transaction_info.dart +++ b/cw_evm/lib/evm_chain_transaction_info.dart @@ -20,6 +20,7 @@ abstract class EVMChainTransactionInfo extends TransactionInfo { required this.confirmations, required this.to, required this.from, + this.evmSignatureName, }) : amount = ethAmount.toInt(), fee = ethFee.toInt(); @@ -38,6 +39,7 @@ abstract class EVMChainTransactionInfo extends TransactionInfo { String? _fiatAmount; final String? to; final String? from; + final String? evmSignatureName; //! Getter to be overridden in child classes String get feeCurrency; @@ -73,5 +75,6 @@ abstract class EVMChainTransactionInfo extends TransactionInfo { 'tokenSymbol': tokenSymbol, 'to': to, 'from': from, + 'evmSignatureName': evmSignatureName, }; } diff --git a/cw_evm/lib/evm_chain_transaction_model.dart b/cw_evm/lib/evm_chain_transaction_model.dart index dfdeab8f5..ca4fb0864 100644 --- a/cw_evm/lib/evm_chain_transaction_model.dart +++ b/cw_evm/lib/evm_chain_transaction_model.dart @@ -12,6 +12,8 @@ class EVMChainTransactionModel { final String? tokenSymbol; final int? tokenDecimal; final bool isError; + final String input; + String? evmSignatureName; EVMChainTransactionModel({ required this.date, @@ -27,6 +29,8 @@ class EVMChainTransactionModel { required this.tokenSymbol, required this.tokenDecimal, required this.isError, + required this.input, + this.evmSignatureName, }); factory EVMChainTransactionModel.fromJson(Map json, String defaultSymbol) => @@ -44,5 +48,7 @@ class EVMChainTransactionModel { tokenSymbol: json["tokenSymbol"] ?? defaultSymbol, tokenDecimal: int.tryParse(json["tokenDecimal"] ?? ""), isError: json["isError"] == "1", + input: json["input"] ?? "", + evmSignatureName: json["evmSignatureName"], ); } diff --git a/cw_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart index 56b58d400..2adb54746 100644 --- a/cw_evm/lib/evm_chain_wallet.dart +++ b/cw_evm/lib/evm_chain_wallet.dart @@ -39,6 +39,20 @@ import 'evm_erc20_balance.dart'; part 'evm_chain_wallet.g.dart'; +const Map methodSignatureToType = { + '0x095ea7b3': 'approval', + '0xa9059cbb': 'transfer', + '0x23b872dd': 'transferFrom', + '0x574da717': 'transferOut', + '0x2e1a7d4d': 'withdraw', + '0x7ff36ab5': 'swapExactETHForTokens', + '0x40c10f19': 'mint', + '0x44bc937b': 'depositWithExpiry', + '0xd0e30db0': 'deposit', + '0xe8e33700': 'addLiquidity', + '0xd505accf': 'permit', +}; + abstract class EVMChainWallet = EVMChainWalletBase with _$EVMChainWallet; abstract class EVMChainWalletBase @@ -235,7 +249,8 @@ abstract class EVMChainWalletBase String? hexOpReturnMemo; if (opReturnMemo != null) { - hexOpReturnMemo = '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}'; + hexOpReturnMemo = + '0x${opReturnMemo.codeUnits.map((char) => char.toRadixString(16).padLeft(2, '0')).join()}'; } final CryptoCurrency transactionCurrency = @@ -337,11 +352,21 @@ abstract class EVMChainWalletBase @override Future> fetchTransactions() async { + final List transactions = []; + final List>> erc20TokensTransactions = []; + final address = _evmChainPrivateKey.address.hex; - final transactions = await _client.fetchTransactions(address); + final externalTransactions = await _client.fetchTransactions(address); final internalTransactions = await _client.fetchInternalTransactions(address); - final List>> erc20TokensTransactions = []; + for (var transaction in externalTransactions) { + final evmSignatureName = analyzeTransaction(transaction.input); + + if (evmSignatureName != 'depositWithExpiry' && evmSignatureName != 'transfer') { + transaction.evmSignatureName = evmSignatureName; + transactions.add(transaction); + } + } for (var token in balance.keys) { if (token is Erc20Token) { @@ -369,6 +394,17 @@ abstract class EVMChainWalletBase return result; } + String? analyzeTransaction(String? transactionInput) { + if (transactionInput == '0x' || transactionInput == null || transactionInput.isEmpty) { + return 'simpleTransfer'; + } + + final methodSignature = + transactionInput.length >= 10 ? transactionInput.substring(0, 10) : null; + + return methodSignatureToType[methodSignature]; + } + @override Object get keys => throw UnimplementedError("keys"); @@ -482,11 +518,11 @@ abstract class EVMChainWalletBase await token.delete(); balance.remove(token); - await _removeTokenTransactionsInHistory(token); + await removeTokenTransactionsInHistory(token); _updateBalance(); } - Future _removeTokenTransactionsInHistory(Erc20Token token) async { + Future removeTokenTransactionsInHistory(Erc20Token token) async { transactionHistory.transactions.removeWhere((key, value) => value.tokenSymbol == token.title); await transactionHistory.save(); } diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index c270bb113..8af6b653c 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -108,6 +108,8 @@ abstract class MoneroWalletBase publicSpendKey: monero_wallet.getPublicSpendKey(), publicViewKey: monero_wallet.getPublicViewKey()); + int? get restoreHeight => transactionHistory.transactions.values.firstOrNull?.height; + monero_wallet.SyncListener? _listener; ReactionDisposer? _onAccountChangeReaction; bool _isTransactionUpdating; diff --git a/cw_monero/pubspec.yaml b/cw_monero/pubspec.yaml index c49a541ab..56f5d2fa6 100644 --- a/cw_monero/pubspec.yaml +++ b/cw_monero/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter_mobx: ^2.0.6+1 intl: ^0.18.0 encrypt: ^5.0.1 - polyseed: ^0.0.2 + polyseed: ^0.0.5 cw_core: path: ../cw_core diff --git a/cw_solana/lib/solana_client.dart b/cw_solana/lib/solana_client.dart index 6ed8cab29..38f2864df 100644 --- a/cw_solana/lib/solana_client.dart +++ b/cw_solana/lib/solana_client.dart @@ -456,7 +456,7 @@ class SolanaWalletClient { funder: ownerKeypair, ); } catch (e) { - throw Exception('Insufficient lamports balance to complete this transaction'); + throw Exception('Insufficient SOL balance to complete this transaction'); } // Input by the user diff --git a/cw_tron/lib/tron_http_provider.dart b/cw_tron/lib/tron_http_provider.dart index 58d313378..8a3301f87 100644 --- a/cw_tron/lib/tron_http_provider.dart +++ b/cw_tron/lib/tron_http_provider.dart @@ -20,6 +20,7 @@ class TronHTTPProvider implements TronServiceProvider { final response = await client.get(Uri.parse(params.url(url)), headers: { 'Content-Type': 'application/json', if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey, + if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey, }).timeout(timeout ?? defaultRequestTimeout); final data = json.decode(response.body) as Map; return data; @@ -32,6 +33,7 @@ class TronHTTPProvider implements TronServiceProvider { headers: { 'Content-Type': 'application/json', if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey, + if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey, }, body: params.toRequestBody()) .timeout(timeout ?? defaultRequestTimeout); diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 000000000..7e7e7f67d --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1 @@ +extensions: diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 15d2ffadf..c62030504 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -295,14 +295,7 @@ class CWBitcoin extends Bitcoin { List types = await compareDerivationMethods(mnemonic: mnemonic, node: node); if (types.length == 1 && types.first == DerivationType.electrum) { - return [ - DerivationInfo( - derivationType: DerivationType.electrum, - derivationPath: "m/0'", - description: "Electrum", - scriptType: "p2wpkh", - ) - ]; + return [getElectrumDerivations()[DerivationType.electrum]!.first]; } final electrumClient = ElectrumClient(); @@ -339,38 +332,34 @@ class CWBitcoin extends Bitcoin { scriptType: dInfo.scriptType, ); - String derivationPath = dInfoCopy.derivationPath!; - int derivationDepth = _countOccurrences(derivationPath, "/"); - - // the correct derivation depth is dependant on the derivation type: - // the derivation paths defined in electrum_derivations are at the ROOT level, i.e.: - // electrum's format doesn't specify subaddresses, just subaccounts: + String balancePath = dInfoCopy.derivationPath!; + int derivationDepth = _countOccurrences(balancePath, "/"); // for BIP44 - if (derivationDepth == 3) { - // we add "/0/0" so that we generate account 0, index 0 and correctly get balance - derivationPath += "/0/0"; + if (derivationDepth == 3 || derivationDepth == 1) { + // we add "/0" so that we generate account 0 + balancePath += "/0"; } - // var hd = bip32.BIP32.fromSeed(seedBytes).derivePath(derivationPath); final hd = btc.HDWallet.fromSeed( seedBytes, network: networkType, - ).derivePath(derivationPath); + ).derivePath(balancePath); + // derive address at index 0: String? address; switch (dInfoCopy.scriptType) { case "p2wpkh": - address = generateP2WPKHAddress(hd: hd, network: network); + address = generateP2WPKHAddress(hd: hd, network: network, index: 0); break; case "p2pkh": - address = generateP2PKHAddress(hd: hd, network: network); + address = generateP2PKHAddress(hd: hd, network: network, index: 0); break; case "p2wpkh-p2sh": - address = generateP2SHAddress(hd: hd, network: network); + address = generateP2SHAddress(hd: hd, network: network, index: 0); break; case "p2tr": - address = generateP2TRAddress(hd: hd, network: network); + address = generateP2TRAddress(hd: hd, network: network, index: 0); break; default: continue; @@ -396,6 +385,11 @@ class CWBitcoin extends Bitcoin { return list; } + @override + Map> getElectrumDerivations() { + return electrum_derivations; + } + @override bool hasTaprootInput(PendingTransaction pendingTransaction) { return (pendingTransaction as PendingBitcoinTransaction).hasTaprootInputs; diff --git a/lib/core/seed_validator.dart b/lib/core/seed_validator.dart index 3e3445757..9fb839ea2 100644 --- a/lib/core/seed_validator.dart +++ b/lib/core/seed_validator.dart @@ -1,15 +1,15 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; -import 'package:cake_wallet/ethereum/ethereum.dart'; -import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/entities/mnemonic_item.dart'; +import 'package:cake_wallet/ethereum/ethereum.dart'; +import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/monero/monero.dart'; -import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/utils/language_list.dart'; +import 'package:cw_core/wallet_type.dart'; class SeedValidator extends Validator { SeedValidator({required this.type, required this.language}) @@ -41,13 +41,16 @@ class SeedValidator extends Validator { return polygon!.getPolygonWordList(language); case WalletType.solana: return solana!.getSolanaWordList(language); - case WalletType.tron: + case WalletType.tron: return tron!.getTronWordList(language); default: return []; } } + static bool needsNormalization(String language) => + ["POLYSEED_French", "POLYSEED_Spanish"].contains(language); + static List getBitcoinWordList(String language) { assert(language.toLowerCase() == LanguageList.english.toLowerCase()); return bitcoin!.getWordList(); diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index dcde1d3ce..891d53f59 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -37,7 +37,7 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002'; const nanoDefaultNodeUri = 'rpc.nano.to'; const nanoDefaultPowNodeUri = 'rpc.nano.to'; const solanaDefaultNodeUri = 'rpc.ankr.com'; -const tronDefaultNodeUri = 'api.trongrid.io'; +const tronDefaultNodeUri = 'trx.nownodes.io'; const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002'; Future defaultSettingsMigration( @@ -233,6 +233,12 @@ Future defaultSettingsMigration( case 36: await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); break; + case 37: + await replaceTronDefaultNode(sharedPreferences: sharedPreferences, nodes: nodes); + break; + case 38: + await fixBtcDerivationPaths(walletInfoSource); + break; default: break; } @@ -775,6 +781,19 @@ Future changeDefaultMoneroNode( } } +Future fixBtcDerivationPaths(Box walletsInfoSource) async { + for (WalletInfo walletInfo in walletsInfoSource.values) { + if (walletInfo.type == WalletType.bitcoin || + walletInfo.type == WalletType.bitcoinCash || + walletInfo.type == WalletType.litecoin) { + if (walletInfo.derivationInfo?.derivationPath == "m/0'/0") { + walletInfo.derivationInfo!.derivationPath = "m/0'"; + await walletInfo.save(); + } + } + } +} + Future updateBtcNanoWalletInfos(Box walletsInfoSource) async { for (WalletInfo walletInfo in walletsInfoSource.values) { if (walletInfo.type == WalletType.nano || walletInfo.type == WalletType.bitcoin) { @@ -1127,3 +1146,29 @@ Future changeTronCurrentNodeToDefault( await sharedPreferences.setInt(PreferencesKey.currentTronNodeIdKey, nodeId); } + +Future replaceTronDefaultNode({ + required SharedPreferences sharedPreferences, + required Box nodes, +}) async { + // Get the currently active node + final currentTronNodeId = sharedPreferences.getInt(PreferencesKey.currentTronNodeIdKey); + final currentTronNode = + nodes.values.firstWhereOrNull((Node node) => node.key == currentTronNodeId); + + //Confirm if this node is part of the default nodes from CakeWallet + final tronDefaultNodeList = [ + 'tron-rpc.publicnode.com:443', + 'api.trongrid.io', + ]; + bool needsToBeReplaced = + currentTronNode == null ? true : tronDefaultNodeList.contains(currentTronNode.uriRaw); + + // If it's a custom node, return. We don't want to switch users from their custom nodes + if (!needsToBeReplaced) { + return; + } + + // If it's not, we switch user to the new default node: NowNodes + await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); +} \ No newline at end of file diff --git a/lib/entities/parse_address_from_domain.dart b/lib/entities/parse_address_from_domain.dart index 409724c6e..c95ce9847 100644 --- a/lib/entities/parse_address_from_domain.dart +++ b/lib/entities/parse_address_from_domain.dart @@ -201,7 +201,7 @@ class AddressResolver { final txtRecord = await OpenaliasRecord.lookupOpenAliasRecord(formattedName); if (txtRecord != null) { final record = await OpenaliasRecord.fetchAddressAndName( - formattedName: formattedName, ticker: ticker, txtRecord: txtRecord); + formattedName: formattedName, ticker: ticker.toLowerCase(), txtRecord: txtRecord); return ParsedAddress.fetchOpenAliasAddress(record: record, name: text); } } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index aebf9ccd5..fdcd54c9c 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -77,6 +77,7 @@ class PreferencesKey { static const moneroSeedType = 'monero_seed_type'; static const clearnetDonationLink = 'clearnet_donation_link'; static const onionDonationLink = 'onion_donation_link'; + static const donationLinkWalletName = 'donation_link_wallet_name'; static const lastSeenAppVersion = 'last_seen_app_version'; static const shouldShowMarketPlaceInDashboard = 'should_show_marketplace_in_dashboard'; static const isNewInstall = 'is_new_install'; diff --git a/lib/ethereum/cw_ethereum.dart b/lib/ethereum/cw_ethereum.dart index e72108e79..7b593d58d 100644 --- a/lib/ethereum/cw_ethereum.dart +++ b/lib/ethereum/cw_ethereum.dart @@ -137,6 +137,10 @@ class CWEthereum extends Ethereum { Future deleteErc20Token(WalletBase wallet, CryptoCurrency token) async => await (wallet as EthereumWallet).deleteErc20Token(token as Erc20Token); + @override + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async => + await (wallet as EthereumWallet).removeTokenTransactionsInHistory(token as Erc20Token); + @override Future getErc20Token(WalletBase wallet, String contractAddress) async { final ethereumWallet = wallet as EthereumWallet; diff --git a/lib/main.dart b/lib/main.dart index 5bcd9ffdb..fbe77bd31 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -202,7 +202,7 @@ Future initializeAppConfigs() async { transactionDescriptions: transactionDescriptions, secureStorage: secureStorage, anonpayInvoiceInfo: anonpayInvoiceInfo, - initialMigrationVersion: 36, + initialMigrationVersion: 38, ); } diff --git a/lib/monero/cw_monero.dart b/lib/monero/cw_monero.dart index 959ae92ce..b4d85089a 100644 --- a/lib/monero/cw_monero.dart +++ b/lib/monero/cw_monero.dart @@ -244,6 +244,12 @@ class CWMonero extends Monero { }; } + @override + int? getRestoreHeight(Object wallet) { + final moneroWallet = wallet as MoneroWallet; + return moneroWallet.restoreHeight; + } + @override Object createMoneroTransactionCreationCredentials( {required List outputs, required TransactionPriority priority}) => diff --git a/lib/polygon/cw_polygon.dart b/lib/polygon/cw_polygon.dart index 16aba284d..2dcb1b4a6 100644 --- a/lib/polygon/cw_polygon.dart +++ b/lib/polygon/cw_polygon.dart @@ -135,6 +135,10 @@ class CWPolygon extends Polygon { Future deleteErc20Token(WalletBase wallet, CryptoCurrency token) async => await (wallet as PolygonWallet).deleteErc20Token(token as Erc20Token); + @override + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token) async => + await (wallet as PolygonWallet).removeTokenTransactionsInHistory(token as Erc20Token); + @override Future getErc20Token(WalletBase wallet, String contractAddress) async { final polygonWallet = wallet as PolygonWallet; diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index c322843ef..3ac97740d 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -3,7 +3,6 @@ import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart'; import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/receive_page_option.dart'; @@ -14,7 +13,6 @@ import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/share_util.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cw_core/wallet_type.dart'; @@ -171,8 +169,7 @@ class AddressPage extends BasePage { textSize: 14, height: 50, ); - } - else { + } else { return const SizedBox(); } }), @@ -204,8 +201,12 @@ class AddressPage extends BasePage { final sharedPreferences = getIt.get(); final clearnetUrl = sharedPreferences.getString(PreferencesKey.clearnetDonationLink); final onionUrl = sharedPreferences.getString(PreferencesKey.onionDonationLink); + final donationWalletName = + sharedPreferences.getString(PreferencesKey.donationLinkWalletName); - if (clearnetUrl != null && onionUrl != null) { + if (clearnetUrl != null && + onionUrl != null && + addressListViewModel.wallet.name == donationWalletName) { Navigator.pushNamed( context, Routes.anonPayReceivePage, diff --git a/lib/src/screens/dashboard/pages/transactions_page.dart b/lib/src/screens/dashboard/pages/transactions_page.dart index e5c94415f..efdd764f1 100644 --- a/lib/src/screens/dashboard/pages/transactions_page.dart +++ b/lib/src/screens/dashboard/pages/transactions_page.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.d import 'package:cake_wallet/view_model/dashboard/order_list_item.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -87,6 +88,10 @@ class TransactionsPage extends StatelessWidget { } final transaction = item.transaction; + final transactionType = dashboardViewModel.type == WalletType.ethereum && + transaction.evmSignatureName == 'approval' + ? ' (${transaction.evmSignatureName})' + : ''; return Observer( builder: (_) => TransactionRow( @@ -100,7 +105,8 @@ class TransactionsPage extends StatelessWidget { ? '' : item.formattedFiatAmount, isPending: transaction.isPending, - title: item.formattedTitle + item.formattedStatus, + title: item.formattedTitle + + item.formattedStatus + ' $transactionType', ), ); } diff --git a/lib/src/screens/receive/anonpay_invoice_page.dart b/lib/src/screens/receive/anonpay_invoice_page.dart index f33cdcc5b..2c66b95db 100644 --- a/lib/src/screens/receive/anonpay_invoice_page.dart +++ b/lib/src/screens/receive/anonpay_invoice_page.dart @@ -60,8 +60,7 @@ class AnonPayInvoicePage extends BasePage { @override Widget middle(BuildContext context) => PresentReceiveOptionPicker( - receiveOptionViewModel: receiveOptionViewModel, - color: titleColor(context)); + receiveOptionViewModel: receiveOptionViewModel, color: titleColor(context)); @override Widget trailing(BuildContext context) => TrailButton( @@ -87,30 +86,36 @@ class AnonPayInvoicePage extends BasePage { config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardBarColor: Theme.of(context).extension()!.keyboardBarColor, - nextFocus: false, - actions: [ - KeyboardActionsItem( - focusNode: _amountFocusNode, - toolbarButtons: [(_) => KeyboardDoneButton()], - ), - ]), - child: Container( - color: Theme.of(context).colorScheme.background, - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 24), - content: Container( - decoration: responsiveLayoutUtil.shouldRenderMobileUI ? BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), - gradient: LinearGradient( - colors: [ - Theme.of(context).extension()!.firstGradientTopPanelColor, - Theme.of(context).extension()!.secondGradientTopPanelColor, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + nextFocus: false, + actions: [ + KeyboardActionsItem( + focusNode: _amountFocusNode, + toolbarButtons: [(_) => KeyboardDoneButton()], ), - ) : null, + ]), + child: Container( + color: Theme.of(context).colorScheme.background, + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.only(bottom: 24), + content: Container( + decoration: responsiveLayoutUtil.shouldRenderMobileUI + ? BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), + gradient: LinearGradient( + colors: [ + Theme.of(context) + .extension()! + .firstGradientTopPanelColor, + Theme.of(context) + .extension()! + .secondGradientTopPanelColor, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ) + : null, child: Observer(builder: (_) { return Padding( padding: EdgeInsets.fromLTRB(24, 120, 24, 0), @@ -143,9 +148,11 @@ class AnonPayInvoicePage extends BasePage { : S.of(context).anonpay_description("a donation link", "donate"), textAlign: TextAlign.center, style: TextStyle( - color: Theme.of(context).extension()!.receiveAmountColor, - fontWeight: FontWeight.w500, - fontSize: 12), + color: Theme.of(context) + .extension()! + .receiveAmountColor, + fontWeight: FontWeight.w500, + fontSize: 12), ), ), ), @@ -172,7 +179,7 @@ class AnonPayInvoicePage extends BasePage { anonInvoicePageViewModel.generateDonationLink(); } }, - color: Theme.of(context).primaryColor, + color: Theme.of(context).primaryColor, textColor: Colors.white, isLoading: anonInvoicePageViewModel.state is IsExecutingState, ), @@ -199,8 +206,12 @@ class AnonPayInvoicePage extends BasePage { final sharedPreferences = getIt.get(); final clearnetUrl = sharedPreferences.getString(PreferencesKey.clearnetDonationLink); final onionUrl = sharedPreferences.getString(PreferencesKey.onionDonationLink); + final donationWalletName = + sharedPreferences.getString(PreferencesKey.donationLinkWalletName); - if (clearnetUrl != null && onionUrl != null) { + if (clearnetUrl != null && + onionUrl != null && + anonInvoicePageViewModel.currentWalletName == donationWalletName) { Navigator.pushReplacementNamed(context, Routes.anonPayReceivePage, arguments: AnonpayDonationLinkInfo( clearnetUrl: clearnetUrl, diff --git a/lib/src/widgets/seed_widget.dart b/lib/src/widgets/seed_widget.dart index 744d5e0f3..72c7ec93a 100644 --- a/lib/src/widgets/seed_widget.dart +++ b/lib/src/widgets/seed_widget.dart @@ -1,11 +1,11 @@ +import 'package:cake_wallet/core/seed_validator.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/src/widgets/validable_annotated_editable_text.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:cake_wallet/core/seed_validator.dart'; -import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; class SeedWidget extends StatefulWidget { SeedWidget({ @@ -24,7 +24,6 @@ class SeedWidget extends StatefulWidget { } class SeedWidgetState extends State { - SeedWidgetState(String language, this.type) : controller = TextEditingController(), focusNode = FocusNode(), @@ -47,6 +46,7 @@ class SeedWidgetState extends State { final FocusNode focusNode; final WalletType type; List words; + bool normalizeSeed = false; bool _showPlaceholder; String get text => controller.text; @@ -61,6 +61,7 @@ class SeedWidgetState extends State { void changeSeedLanguage(String language) { setState(() { words = SeedValidator.getWordList(type: type, language: language); + normalizeSeed = SeedValidator.needsNormalization(language); }); } @@ -99,6 +100,7 @@ class SeedWidgetState extends State { focusNode: focusNode, controller: controller, words: words, + normalizeSeed: normalizeSeed, textStyle: TextStyle( color: Theme.of(context).extension()!.titleColor, backgroundColor: Colors.transparent, diff --git a/lib/src/widgets/validable_annotated_editable_text.dart b/lib/src/widgets/validable_annotated_editable_text.dart index 134eb16a8..a7777961d 100644 --- a/lib/src/widgets/validable_annotated_editable_text.dart +++ b/lib/src/widgets/validable_annotated_editable_text.dart @@ -1,6 +1,6 @@ +import 'package:cw_core/utils/text_normalizer.dart'; import 'package:flutter/material.dart'; - extension Compare on Comparable { bool operator <=(T other) => compareTo(other) <= 0; bool operator >=(T other) => compareTo(other) >= 0; @@ -39,6 +39,7 @@ class ValidatableAnnotatedEditableText extends EditableText { required this.validStyle, required this.invalidStyle, required this.words, + this.normalizeSeed = false, TextStyle textStyle = const TextStyle( color: Colors.black, backgroundColor: Colors.transparent, @@ -74,6 +75,7 @@ class ValidatableAnnotatedEditableText extends EditableText { showSelectionHandles: true, showCursor: true); + final bool normalizeSeed; final List words; final TextStyle validStyle; final TextStyle invalidStyle; @@ -137,7 +139,8 @@ class ValidatableAnnotatedEditableTextState extends EditableTextState { return result; } - bool validate(String source) => widget.words.indexOf(source) >= 0; + bool validate(String source) => + widget.words.indexOf(widget.normalizeSeed ? normalizeText(source) : source) >= 0; List range(String pattern, String source) { final result = []; @@ -173,4 +176,4 @@ class ValidatableAnnotatedEditableTextState extends EditableTextState { return TextSpan(style: widget.style, text: text); } -} \ No newline at end of file +} diff --git a/lib/view_model/anon_invoice_page_view_model.dart b/lib/view_model/anon_invoice_page_view_model.dart index 187eea375..39992dca7 100644 --- a/lib/view_model/anon_invoice_page_view_model.dart +++ b/lib/view_model/anon_invoice_page_view_model.dart @@ -150,6 +150,7 @@ abstract class AnonInvoicePageViewModelBase with Store { await sharedPreferences.setString(PreferencesKey.clearnetDonationLink, result.clearnetUrl); await sharedPreferences.setString(PreferencesKey.onionDonationLink, result.onionUrl); + await sharedPreferences.setString(PreferencesKey.donationLinkWalletName, _wallet.name); state = ExecutedSuccessfullyState(payload: result); } @@ -163,10 +164,13 @@ abstract class AnonInvoicePageViewModelBase with Store { maximum = limit.max != null ? limit.max! / 4 : null; } + @computed + String get currentWalletName => _wallet.name; + @action void reset() { selectedCurrency = walletTypeToCryptoCurrency(_wallet.type); - cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type); + cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type); receipientEmail = ''; receipientName = ''; description = ''; @@ -177,7 +181,10 @@ abstract class AnonInvoicePageViewModelBase with Store { Future _getPreviousDonationLink() async { if (pageOption == ReceivePageOption.anonPayDonationLink) { final donationLink = sharedPreferences.getString(PreferencesKey.clearnetDonationLink); - if (donationLink != null) { + final donationLinkWalletName = + sharedPreferences.getString(PreferencesKey.donationLinkWalletName); + + if (donationLink != null && currentWalletName == donationLinkWalletName) { final url = Uri.parse(donationLink); url.queryParameters.forEach((key, value) { if (key == 'name') receipientName = value; diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index 8dbd97bb9..d63f78224 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -39,7 +39,7 @@ abstract class ContactListViewModelBase with Store { )); } }); - } else if (info.addresses?.isNotEmpty == true) { + } else if (info.addresses?.isNotEmpty == true && info.addresses!.length > 1) { info.addresses!.forEach((address, label) { if (label.isEmpty) { return; diff --git a/lib/view_model/dashboard/home_settings_view_model.dart b/lib/view_model/dashboard/home_settings_view_model.dart index 9e3be746e..5778f1e19 100644 --- a/lib/view_model/dashboard/home_settings_view_model.dart +++ b/lib/view_model/dashboard/home_settings_view_model.dart @@ -144,10 +144,12 @@ abstract class HomeSettingsViewModelBase with Store { if (_balanceViewModel.wallet.type == WalletType.ethereum) { ethereum!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token); + if (!value) ethereum!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token); } if (_balanceViewModel.wallet.type == WalletType.polygon) { polygon!.addErc20Token(_balanceViewModel.wallet, token as Erc20Token); + if (!value) polygon!.removeTokenTransactionsInHistory(_balanceViewModel.wallet, token); } if (_balanceViewModel.wallet.type == WalletType.solana) { diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index f825f0c47..841a88e7e 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; @@ -98,10 +99,7 @@ abstract class WalletCreationVMBase with Store { ); case WalletType.bitcoin: case WalletType.litecoin: - return DerivationInfo( - derivationType: DerivationType.electrum, - derivationPath: "m/0'", - ); + return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first; default: return null; } diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index 6b5ae5559..060770273 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -1,16 +1,15 @@ -import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/reactions/wallet_connect.dart'; +import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_info.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/generated/i18n.dart'; import 'package:cw_core/wallet_base.dart'; -import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; -import 'package:cake_wallet/monero/monero.dart'; -import 'package:cake_wallet/haven/haven.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:cw_monero/api/wallet.dart' as monero_wallet; +import 'package:mobx/mobx.dart'; import 'package:polyseed/polyseed.dart'; part 'wallet_keys_view_model.g.dart'; @@ -83,6 +82,12 @@ abstract class WalletKeysViewModelBase with Store { .toLegacySeed(legacyLang); items.add(StandartListItem(title: S.current.wallet_seed_legacy, value: legacySeed)); } + + final restoreHeight = monero!.getRestoreHeight(_appStore.wallet!); + if (restoreHeight != null) { + items.add(StandartListItem( + title: S.current.wallet_recovery_height, value: restoreHeight.toString())); + } } if (_appStore.wallet!.type == WalletType.haven) { diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 8bb4d45c7..62a6bbc85 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -60,8 +60,6 @@ dependencies: git: url: https://github.com/cake-tech/flutter_file_picker.git ref: master - unorm_dart: ^0.2.0 - # check unorm_dart for usage and for replace permission_handler: ^10.0.0 device_display_brightness: git: @@ -100,7 +98,7 @@ dependencies: # ref: main socks5_proxy: ^1.0.4 flutter_svg: ^2.0.9 - polyseed: ^0.0.4 + polyseed: ^0.0.5 nostr_tools: ^1.0.9 solana: ^0.30.1 bitcoin_base: diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index fa2747230..db6a4cae2 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -834,6 +834,7 @@ "wallet_menu": "قائمة", "wallet_name": "اسم المحفظة", "wallet_name_exists": "توجد بالفعل محفظة بهذا الاسم. الرجاء اختيار اسم مختلف أو إعادة تسمية المحفظة الأخرى أولاً.", + "wallet_recovery_height": "ارتفاع الاسترداد", "wallet_restoration_store_incorrect_seed_length": "طول السييد غير صحيح", "wallet_seed": "سييد المحفظة", "wallet_seed_legacy": "بذرة محفظة قديمة", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 430a26134..06132c244 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -834,6 +834,7 @@ "wallet_menu": "Меню", "wallet_name": "Име на портфейл", "wallet_name_exists": "Вече има портфейл с това име. Моля, изберете друго име или преименувайте другия портфейл.", + "wallet_recovery_height": "Височина на възстановяване", "wallet_restoration_store_incorrect_seed_length": "Грешна дължина на seed-а", "wallet_seed": "Seed на портфейла", "wallet_seed_legacy": "Наследено портфейл семе", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 6a92cb2e5..589f89fd7 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -834,6 +834,7 @@ "wallet_menu": "Menu", "wallet_name": "Název peněženky", "wallet_name_exists": "Peněženka s tímto názvem už existuje. Prosím zvolte si jiný název, nebo nejprve přejmenujte nejprve druhou peněženku.", + "wallet_recovery_height": "Výška zotavení", "wallet_restoration_store_incorrect_seed_length": "Nesprávná délka seedu", "wallet_seed": "Seed peněženky", "wallet_seed_legacy": "Starší semeno peněženky", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 2e4203edd..a4b816f5b 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -837,6 +837,7 @@ "wallet_menu": "Wallet-Menü", "wallet_name": "Walletname", "wallet_name_exists": "Wallet mit diesem Namen existiert bereits", + "wallet_recovery_height": "Erstellungshöhe", "wallet_restoration_store_incorrect_seed_length": "Falsche Seed-Länge", "wallet_seed": "Wallet-Seed", "wallet_seed_legacy": "Legacy Wallet Seed", @@ -875,4 +876,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} \ No newline at end of file +} diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 1b3da32ab..ac82c9e0f 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -834,6 +834,7 @@ "wallet_menu": "Menu", "wallet_name": "Wallet name", "wallet_name_exists": "A wallet with that name already exists. Please choose a different name or rename the other wallet first.", + "wallet_recovery_height": "Recovery Height", "wallet_restoration_store_incorrect_seed_length": "Incorrect seed length", "wallet_seed": "Wallet seed", "wallet_seed_legacy": "Legacy wallet seed", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 215cece6e..5fba46830 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -835,6 +835,7 @@ "wallet_menu": "Menú de billetera", "wallet_name": "Nombre de la billetera", "wallet_name_exists": "Wallet con ese nombre ya ha existido", + "wallet_recovery_height": "Altura de recuperación", "wallet_restoration_store_incorrect_seed_length": "Longitud de semilla incorrecta", "wallet_seed": "Semilla de billetera", "wallet_seed_legacy": "Semilla de billetera heredada", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 997dbc38c..7289b7511 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -266,7 +266,7 @@ "errorSigningTransaction": "Une erreur s'est produite lors de la signature de la transaction", "estimated": "Estimé", "estimated_new_fee": "De nouveaux frais estimés", - "etherscan_history": "Historique d'Etherscan", + "etherscan_history": "Historique Etherscan", "event": "Événement", "events": "Événements", "exchange": "Échanger", @@ -404,7 +404,7 @@ "node_test": "Tester", "nodes": "Nœuds", "nodes_list_reset_to_default_message": "Êtes vous certain de vouloir revenir aux réglages par défaut ?", - "none_of_selected_providers_can_exchange": "Aucun des prestataires sélectionnés ne peut effectuer cet échange", + "none_of_selected_providers_can_exchange": "Aucun des fournisseurs sélectionnés ne peut effectuer cet échange", "noNFTYet": "Pas encore de NFT", "normal": "Normal", "note_optional": "Note (optionnelle)", @@ -794,7 +794,7 @@ "unconfirmed": "Solde non confirmé", "understand": "J'ai compris", "unmatched_currencies": "La devise de votre portefeuille (wallet) actuel ne correspond pas à celle du QR code scanné", - "unspent_change": "Changement", + "unspent_change": "Monnaie", "unspent_coins_details_title": "Détails des pièces (coins) non dépensées", "unspent_coins_title": "Pièces (coins) non dépensées", "unsupported_asset": "Nous ne prenons pas en charge cette action pour cet élément. Veuillez créer ou passer à un portefeuille d'un type d'actif pris en charge.", @@ -834,6 +834,7 @@ "wallet_menu": "Menu", "wallet_name": "Nom du Portefeuille (Wallet)", "wallet_name_exists": "Un portefeuille (wallet) portant ce nom existe déjà", + "wallet_recovery_height": "Hauteur de récupération", "wallet_restoration_store_incorrect_seed_length": "Longueur de phrase secrète (seed) incorrecte", "wallet_seed": "Phrase secrète (seed) du portefeuille (wallet)", "wallet_seed_legacy": "Graine de portefeuille hérité", @@ -872,4 +873,4 @@ "you_will_get": "Convertir vers", "you_will_send": "Convertir depuis", "yy": "AA" -} \ No newline at end of file +} diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 478bc458d..c96568a76 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -836,6 +836,7 @@ "wallet_menu": "Menu", "wallet_name": "Sunan walat", "wallet_name_exists": "Wallet mai wannan sunan ya riga ya wanzu. Da fatan za a zaɓi wani suna daban ko sake suna ɗayan walat tukuna.", + "wallet_recovery_height": "Mai Tsaro", "wallet_restoration_store_incorrect_seed_length": "kalmar sirrin iri ba daidai ba", "wallet_seed": "kalmar sirri na walat", "wallet_seed_legacy": "Tallarin walat walat", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 4f3a77fdb..ef740b4d6 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -836,6 +836,7 @@ "wallet_menu": "बटुआ मेनू", "wallet_name": "बटुए का नाम", "wallet_name_exists": "उस नाम वाला वॉलेट पहले से मौजूद है", + "wallet_recovery_height": "वसूली ऊंचाई", "wallet_restoration_store_incorrect_seed_length": "गलत बीज की लंबाई", "wallet_seed": "बटुआ का बीज", "wallet_seed_legacy": "विरासत बटुए बीज", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index faef9cd78..295a01165 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -834,6 +834,7 @@ "wallet_menu": "Izbornik", "wallet_name": "Ime novčanika", "wallet_name_exists": "Novčanik s tim nazivom već postoji", + "wallet_recovery_height": "Visina oporavka", "wallet_restoration_store_incorrect_seed_length": "Netočna dužina pristupnog izraza", "wallet_seed": "Pristupni izraz novčanika", "wallet_seed_legacy": "Sjeme naslijeđenog novčanika", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index e718f24da..8b28e928f 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -837,6 +837,7 @@ "wallet_menu": "Menu", "wallet_name": "Nama Dompet", "wallet_name_exists": "Nama dompet sudah ada. Silakan pilih nama yang berbeda atau ganti nama dompet yang lain terlebih dahulu.", + "wallet_recovery_height": "Tinggi pemulihan", "wallet_restoration_store_incorrect_seed_length": "Panjang seed yang salah", "wallet_seed": "Seed dompet", "wallet_seed_legacy": "Biji dompet warisan", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 6a25ad9e0..b1bd7cd6d 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -837,6 +837,7 @@ "wallet_menu": "Menù", "wallet_name": "Nome del Portafoglio", "wallet_name_exists": "Il portafoglio con quel nome è già esistito", + "wallet_recovery_height": "Altezza di recupero", "wallet_restoration_store_incorrect_seed_length": "Lunghezza seme non corretta", "wallet_seed": "Seme Portafoglio", "wallet_seed_legacy": "Seme di portafoglio legacy", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 56786cabc..5803b86d5 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -835,6 +835,7 @@ "wallet_menu": "ウォレットメニュー", "wallet_name": "ウォレット名", "wallet_name_exists": "その名前のウォレットはすでに存在しています", + "wallet_recovery_height": "回復の高さ", "wallet_restoration_store_incorrect_seed_length": "誤ったシード長s", "wallet_seed": "ウォレットシード", "wallet_seed_legacy": "レガシーウォレットシード", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 5de972a6e..874ec2c27 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -835,6 +835,7 @@ "wallet_menu": "월렛 메뉴", "wallet_name": "지갑 이름", "wallet_name_exists": "해당 이름의 지갑이 이미 존재합니다.", + "wallet_recovery_height": "복구 높이", "wallet_restoration_store_incorrect_seed_length": "시드 길이가 잘못되었습니다", "wallet_seed": "지갑 시드", "wallet_seed_legacy": "레거시 지갑 시드", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 497e1bb0c..890362fcd 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -834,6 +834,7 @@ "wallet_menu": "မီနူး", "wallet_name": "ပိုက်ဆံအိတ်နာမည", "wallet_name_exists": "ထိုအမည်ဖြင့် ပိုက်ဆံအိတ်တစ်ခု ရှိနှင့်ပြီးဖြစ်သည်။ အခြားအမည်တစ်ခုကို ရွေးပါ သို့မဟုတ် အခြားပိုက်ဆံအိတ်ကို ဦးစွာ အမည်ပြောင်းပါ။", + "wallet_recovery_height": "ပြန်လည်ထူထောင်ရေးအမြင့်", "wallet_restoration_store_incorrect_seed_length": "မျိုးစေ့အရှည် မမှန်ပါ။", "wallet_seed": "ပိုက်ဆံအိတ်စေ့", "wallet_seed_legacy": "အမွေအနှစ်ပိုက်ဆံအိတ်မျိုးစေ့", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 1bc1f8cd7..25b7f6b3a 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -835,6 +835,7 @@ "wallet_menu": "Portemonnee-menu", "wallet_name": "Portemonnee naam", "wallet_name_exists": "Portemonnee met die naam bestaat al", + "wallet_recovery_height": "Herstelhoogte", "wallet_restoration_store_incorrect_seed_length": "Onjuiste zaadlengte", "wallet_seed": "Portemonnee zaad", "wallet_seed_legacy": "Legacy portemonnee zaad", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index ff8826c1e..1567a0841 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -834,6 +834,7 @@ "wallet_menu": "Menu portfela", "wallet_name": "Nazwa portfela", "wallet_name_exists": "Portfel o tej nazwie już istnieje", + "wallet_recovery_height": "Wysokość odzysku", "wallet_restoration_store_incorrect_seed_length": "Nieprawidłowa długość frazy seed", "wallet_seed": "Seed portfela", "wallet_seed_legacy": "Dziedziczne ziarno portfela", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 67ce2980a..272c0862e 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -837,6 +837,7 @@ "wallet_menu": "Menu", "wallet_name": "Nome da carteira", "wallet_name_exists": "A carteira com esse nome já existe", + "wallet_recovery_height": "Altura de recuperação", "wallet_restoration_store_incorrect_seed_length": "Comprimento de semente incorreto", "wallet_seed": "Semente de carteira", "wallet_seed_legacy": "Semente de carteira herdada", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 673f1472d..a8943db97 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -835,6 +835,7 @@ "wallet_menu": "Меню кошелька", "wallet_name": "Имя кошелька", "wallet_name_exists": "Кошелек с таким именем уже существует", + "wallet_recovery_height": "Высота восстановления", "wallet_restoration_store_incorrect_seed_length": "Неверная длина мнемонической фразы", "wallet_seed": "Мнемоническая фраза кошелька", "wallet_seed_legacy": "Наследие семя кошелька", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 1779b9da1..c4d282761 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -834,6 +834,7 @@ "wallet_menu": "เมนู", "wallet_name": "ชื่อกระเป๋า", "wallet_name_exists": "กระเป๋าที่มีชื่อนี้มีอยู่แล้ว โปรดเลือกชื่ออื่นหรือเปลี่ยนชื่อกระเป๋าอื่นก่อน", + "wallet_recovery_height": "ความสูงของการกู้คืน", "wallet_restoration_store_incorrect_seed_length": "ความยาวของซีดไม่ถูกต้อง", "wallet_seed": "ซีดของกระเป๋า", "wallet_seed_legacy": "เมล็ดกระเป๋าเงินมรดก", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index e358d6daf..775ab4c3d 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -834,6 +834,7 @@ "wallet_menu": "Menu", "wallet_name": "Pangalan ng Wallet", "wallet_name_exists": "Ang isang pitaka na may pangalang iyon ay mayroon na. Mangyaring pumili ng ibang pangalan o palitan muna ang iba pang pitaka.", + "wallet_recovery_height": "Taas ng pagbawi", "wallet_restoration_store_incorrect_seed_length": "Maling haba ng binhi", "wallet_seed": "SEED ng Wallet", "wallet_seed_legacy": "Legacy wallet seed", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 76bd79d8a..239a1aa2e 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -834,6 +834,7 @@ "wallet_menu": "Menü", "wallet_name": "Cüzdan ismi", "wallet_name_exists": "Bu isimde bir cüzdan zaten mevcut. Lütfen farklı bir isim seç veya önce diğer cüzdanı yeniden adlandır.", + "wallet_recovery_height": "Kurtarma Yüksekliği", "wallet_restoration_store_incorrect_seed_length": "Yanlış tohum uzunluğu", "wallet_seed": "Cüzdan tohumu", "wallet_seed_legacy": "Eski cüzdan tohumu", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index e1d470e1e..3a45752d6 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -835,6 +835,7 @@ "wallet_menu": "Меню гаманця", "wallet_name": "Ім'я гаманця", "wallet_name_exists": "Гаманець з такою назвою вже існує", + "wallet_recovery_height": "Висота відновлення", "wallet_restoration_store_incorrect_seed_length": "Невірна довжина мнемонічної фрази", "wallet_seed": "Мнемонічна фраза гаманця", "wallet_seed_legacy": "Спадець насіння гаманця", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 02c5216ef..2ab1f927b 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -836,6 +836,7 @@ "wallet_menu": "مینو", "wallet_name": "بٹوے کا نام", "wallet_name_exists": "اس نام کا پرس پہلے سے موجود ہے۔ براہ کرم ایک مختلف نام منتخب کریں یا پہلے دوسرے بٹوے کا نام تبدیل کریں۔", + "wallet_recovery_height": "بحالی کی اونچائی", "wallet_restoration_store_incorrect_seed_length": "غلط بیج کی لمبائی", "wallet_seed": "بٹوے کا بیج", "wallet_seed_legacy": "میراثی پرس کا بیج", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 221b189e1..f9751f9f1 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -835,6 +835,7 @@ "wallet_menu": "Mẹ́nù", "wallet_name": "Orúkọ àpamọ́wọ́", "wallet_name_exists": "Ẹ ti ní àpamọ́wọ́ pẹ̀lú orúkọ̀ yẹn. Ẹ jọ̀wọ́ yàn orúkọ̀ tó yàtọ̀ tàbí pààrọ̀ orúkọ ti àpamọ́wọ́ tẹ́lẹ̀.", + "wallet_recovery_height": "Iga Imularada", "wallet_restoration_store_incorrect_seed_length": "Gígùn hóró tí a máa ń lò kọ́ ni èyí", "wallet_seed": "Hóró àpamọ́wọ́", "wallet_seed_legacy": "Irugbin akole", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index d98801cb2..f3b8ee176 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -834,6 +834,7 @@ "wallet_menu": "钱包菜单", "wallet_name": "钱包名称", "wallet_name_exists": "同名的钱包已经存在", + "wallet_recovery_height": "恢复高度", "wallet_restoration_store_incorrect_seed_length": "种子长度错误", "wallet_seed": "钱包种子", "wallet_seed_legacy": "旧的钱包种子", diff --git a/tool/configure.dart b/tool/configure.dart index fc9bd5b91..133f12e52 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -186,6 +186,7 @@ abstract class Bitcoin { {required String mnemonic, required Node node}); Future> getDerivationsFromMnemonic( {required String mnemonic, required Node node, String? passphrase}); + Map> getElectrumDerivations(); Future setAddressType(Object wallet, dynamic option); ReceivePageOption getSelectedAddressType(Object wallet); List getBitcoinReceivePageOptions(); @@ -362,6 +363,7 @@ abstract class Monero { WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String password}); Map getKeys(Object wallet); + int? getRestoreHeight(Object wallet); Object createMoneroTransactionCreationCredentials({required List outputs, required TransactionPriority priority}); Object createMoneroTransactionCreationCredentialsRaw({required List outputs, required TransactionPriority priority}); String formatterMoneroAmountToString({required int amount}); @@ -665,6 +667,7 @@ abstract class Ethereum { List getERC20Currencies(WalletBase wallet); Future addErc20Token(WalletBase wallet, CryptoCurrency token); Future deleteErc20Token(WalletBase wallet, CryptoCurrency token); + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token); Future getErc20Token(WalletBase wallet, String contractAddress); CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction); @@ -768,6 +771,7 @@ abstract class Polygon { List getERC20Currencies(WalletBase wallet); Future addErc20Token(WalletBase wallet, CryptoCurrency token); Future deleteErc20Token(WalletBase wallet, CryptoCurrency token); + Future removeTokenTransactionsInHistory(WalletBase wallet, CryptoCurrency token); Future getErc20Token(WalletBase wallet, String contractAddress); CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction); diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index f75dbb2c0..1d539d95c 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -40,6 +40,10 @@ class SecretKey { SecretKey('ankrApiKey', () => ''), SecretKey('quantexExchangeMarkup', () => ''), SecretKey('seeds', () => ''), + SecretKey('testCakePayApiKey', () => ''), + SecretKey('cakePayApiKey', () => ''), + SecretKey('CSRFToken', () => ''), + SecretKey('authorization', () => ''), ]; static final evmChainsSecrets = [ @@ -55,9 +59,10 @@ class SecretKey { static final nanoSecrets = [ SecretKey('nano2ApiKey', () => ''), ]; - + static final tronSecrets = [ SecretKey('tronGridApiKey', () => ''), + SecretKey('tronNowNodesApiKey', () => ''), ]; final String name;