Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-659-Confirm-Seeds-Display-Correctly

This commit is contained in:
Blazebrain 2024-09-11 05:14:17 +01:00
commit 5779d30a48
118 changed files with 2015 additions and 698 deletions

View file

@ -171,6 +171,10 @@ jobs:
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/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 echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
- name: Rename app - name: Rename app
run: | run: |

View file

@ -154,6 +154,10 @@ jobs:
echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart echo "const nanoNowNodesApiKey = '${{ secrets.NANO_NOW_NODES_API_KEY }}';" >> cw_nano/lib/.secrets.g.dart
echo "const tronGridApiKey = '${{ secrets.TRON_GRID_API_KEY }}';" >> cw_tron/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 echo "const tronNowNodesApiKey = '${{ secrets.TRON_NOW_NODES_API_KEY }}';" >> cw_tron/lib/.secrets.g.dart
echo "const letsExchangeBearerToken = '${{ secrets.LETS_EXCHANGE_TOKEN }}';" >> lib/.secrets.g.dart
echo "const letsExchangeAffiliateId = '${{ secrets.LETS_EXCHANGE_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const stealthExBearerToken = '${{ secrets.STEALTH_EX_BEARER_TOKEN }}';" >> lib/.secrets.g.dart
echo "const stealthExAdditionalFeePercent = '${{ secrets.STEALTH_EX_ADDITIONAL_FEE_PERCENT }}';" >> lib/.secrets.g.dart
- name: Rename app - name: Rename app
run: | run: |

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M16 1.37854C16 0.764286 16.6636 0.379192 17.1969 0.68395L23 4L29.4961 7.71208C29.8077 7.89012 30 8.22147 30 8.58032V16L23.9923 12.567C23.3774 12.2157 22.6226 12.2157 22.0077 12.567L16 16V8V1.37854ZM2 16V8.58032C2 8.22147 2.19229 7.89012 2.50386 7.71208L8.00772 4.56702C8.62259 4.21566 9.37741 4.21566 9.99228 4.56702L16 8L2 16ZM16 30.6215C16 31.2357 15.3364 31.6208 14.8031 31.3161L9 28L2.50386 24.2879C2.19229 24.1099 2 23.7785 2 23.4197V16L8.00772 19.433C8.62259 19.7843 9.37741 19.7843 9.99228 19.433L16 16V24V30.6215ZM22.0077 27.433C22.6226 27.7843 23.3774 27.7843 23.9923 27.433L29.4961 24.2879C29.8077 24.1099 30 23.7785 30 23.4197V16L16 24L22.0077 27.433Z"
fill="#159DFF"></path>
</svg>

After

Width:  |  Height:  |  Size: 846 B

BIN
assets/images/stealthex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

@ -17,6 +17,3 @@
- -
uri: node.community.rino.io:18081 uri: node.community.rino.io:18081
is_default: false is_default: false
-
uri: node.moneroworld.com:18089
is_default: false

View file

@ -1,3 +1,2 @@
Scan and verify messages Enhance auto-address generation for Monero
Synchronization enhancements Bug fixes and enhancements
Bug fixes

View file

@ -1,3 +1,4 @@
Scan and verify messages Enable BIP39 by default for wallet creation also on Bitcoin/Litecoin (Electrum seed type is still accessible through advanced settings page)
Synchronization enhancements Improve fee calculation for Bitcoin to protect against overpaying or underpaying
Bug fixes Enhance auto-address generation for Monero
Bug fixes and enhancements

View file

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:cryptography/cryptography.dart' as cryptography; import 'package:cryptography/cryptography.dart' as cryptography;
import 'package:cw_core/sec_random_native.dart'; import 'package:cw_core/sec_random_native.dart';
@ -59,11 +60,7 @@ void maskBytes(Uint8List bytes, int bits) {
} }
} }
String bufferToBin(Uint8List data) { String bufferToBin(Uint8List data) => data.map((e) => e.toRadixString(2).padLeft(8, '0')).join('');
final q1 = data.map((e) => e.toRadixString(2).padLeft(8, '0'));
final q2 = q1.join('');
return q2;
}
String encode(Uint8List data) { String encode(Uint8List data) {
final dataBitLen = data.length * 8; final dataBitLen = data.length * 8;
@ -112,17 +109,18 @@ Future<bool> checkIfMnemonicIsElectrum2(String mnemonic) async {
Future<String> getMnemonicHash(String mnemonic) async { Future<String> getMnemonicHash(String mnemonic) async {
final hmacSha512 = Hmac(sha512, utf8.encode('Seed version')); final hmacSha512 = Hmac(sha512, utf8.encode('Seed version'));
final digest = hmacSha512.convert(utf8.encode(normalizeText(mnemonic))); final digest = hmacSha512.convert(utf8.encode(normalizeText(mnemonic)));
final hx = digest.toString(); return digest.toString();
return hx;
} }
Future<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async { Future<Uint8List> mnemonicToSeedBytes(String mnemonic,
{String prefix = segwit, String passphrase = ''}) async {
final pbkdf2 = final pbkdf2 =
cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512); cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512);
final text = normalizeText(mnemonic); final text = normalizeText(mnemonic);
// pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce) final passphraseBytes = utf8.encode(normalizeText(passphrase));
final key = await pbkdf2.deriveKey( final key = await pbkdf2.deriveKey(
secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits); secretKey: cryptography.SecretKey(text.codeUnits),
nonce: [...'electrum'.codeUnits, ...passphraseBytes]);
final bytes = await key.extractBytes(); final bytes = await key.extractBytes();
return Uint8List.fromList(bytes); return Uint8List.fromList(bytes);
} }

View file

@ -7,5 +7,6 @@ class MnemonicBip39 {
static String generate({int strength = 128}) => bip39.generateMnemonic(strength: strength); static String generate({int strength = 128}) => bip39.generateMnemonic(strength: strength);
/// Create root seed from mnemonic /// Create root seed from mnemonic
static Uint8List toSeed(String mnemonic) => bip39.mnemonicToSeed(mnemonic); static Uint8List toSeed(String mnemonic, {String? passphrase}) =>
bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
} }

View file

@ -115,7 +115,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
break; break;
case DerivationType.electrum: case DerivationType.electrum:
default: default:
seedBytes = await mnemonicToSeedBytes(mnemonic); seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break; break;
} }
return BitcoinWallet( return BitcoinWallet(
@ -195,7 +195,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
if (mnemonic != null) { if (mnemonic != null) {
switch (walletInfo.derivationInfo!.derivationType) { switch (walletInfo.derivationInfo!.derivationType) {
case DerivationType.electrum: case DerivationType.electrum:
seedBytes = await mnemonicToSeedBytes(mnemonic); seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break; break;
case DerivationType.bip39: case DerivationType.bip39:
default: default:

View file

@ -3,16 +3,18 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
class BitcoinNewWalletCredentials extends WalletCredentials { class BitcoinNewWalletCredentials extends WalletCredentials {
BitcoinNewWalletCredentials( BitcoinNewWalletCredentials({
{required String name, required String name,
WalletInfo? walletInfo, WalletInfo? walletInfo,
String? password, String? password,
DerivationType? derivationType, DerivationType? derivationType,
String? derivationPath}) String? derivationPath,
: super( String? passphrase,
}) : super(
name: name, name: name,
walletInfo: walletInfo, walletInfo: walletInfo,
password: password, password: password,
passphrase: passphrase,
); );
} }

View file

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart';
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/encryption_file_utils.dart';
@ -35,8 +36,21 @@ class BitcoinWalletService extends WalletService<
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet; final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
credentials.walletInfo?.network = network.value; credentials.walletInfo?.network = network.value;
final String mnemonic;
switch ( credentials.walletInfo?.derivationInfo?.derivationType) {
case DerivationType.bip39:
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
mnemonic = await MnemonicBip39.generate(strength: strength);
break;
case DerivationType.electrum:
default:
mnemonic = await generateElectrumMnemonic();
break;
}
final wallet = await BitcoinWalletBase.create( final wallet = await BitcoinWalletBase.create(
mnemonic: await generateElectrumMnemonic(), mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
passphrase: credentials.passphrase, passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,

View file

@ -107,22 +107,19 @@ class ElectrumClient {
} }
}, },
onError: (Object error) { onError: (Object error) {
socket = null;
final errorMsg = error.toString(); final errorMsg = error.toString();
print(errorMsg); print(errorMsg);
unterminatedString = ''; unterminatedString = '';
final currentHost = socket?.address.host;
final isErrorForCurrentHost = errorMsg.contains(" ${currentHost} ");
if (currentHost != null && isErrorForCurrentHost)
_setConnectionStatus(ConnectionStatus.failed);
}, },
onDone: () { onDone: () {
unterminatedString = ''; unterminatedString = '';
if (host == socket?.address.host) { try {
socket = null; if (host == socket?.address.host) {
_setConnectionStatus(ConnectionStatus.disconnected); socket?.destroy();
_setConnectionStatus(ConnectionStatus.disconnected);
}
} catch(e) {
print(e.toString());
} }
}, },
cancelOnError: true, cancelOnError: true,
@ -436,7 +433,6 @@ class ElectrumClient {
{required String id, required String method, List<Object> params = const []}) { {required String id, required String method, List<Object> params = const []}) {
try { try {
if (socket == null) { if (socket == null) {
_setConnectionStatus(ConnectionStatus.failed);
return null; return null;
} }
final subscription = BehaviorSubject<T>(); final subscription = BehaviorSubject<T>();
@ -453,7 +449,6 @@ class ElectrumClient {
Future<dynamic> call( Future<dynamic> call(
{required String method, List<Object> params = const [], Function(int)? idCallback}) async { {required String method, List<Object> params = const [], Function(int)? idCallback}) async {
if (socket == null) { if (socket == null) {
_setConnectionStatus(ConnectionStatus.failed);
return null; return null;
} }
final completer = Completer<dynamic>(); final completer = Completer<dynamic>();
@ -467,10 +462,9 @@ class ElectrumClient {
} }
Future<dynamic> callWithTimeout( Future<dynamic> callWithTimeout(
{required String method, List<Object> params = const [], int timeout = 4000}) async { {required String method, List<Object> params = const [], int timeout = 5000}) async {
try { try {
if (socket == null) { if (socket == null) {
_setConnectionStatus(ConnectionStatus.failed);
return null; return null;
} }
final completer = Completer<dynamic>(); final completer = Completer<dynamic>();

View file

@ -109,5 +109,4 @@ Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
], ],
}; };
String electrum_path = electrum_derivations[DerivationType.electrum]!.first.derivationPath!; String electrum_path = electrum_derivations[DerivationType.electrum]!.first.derivationPath!;

View file

@ -5,6 +5,7 @@ import 'dart:isolate';
import 'dart:math'; import 'dart:math';
import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/encryption_file_utils.dart';
import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@ -44,6 +45,7 @@ import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart'; import 'package:rxdart/subjects.dart';
import 'package:sp_scanner/sp_scanner.dart'; import 'package:sp_scanner/sp_scanner.dart';
import 'package:hex/hex.dart'; import 'package:hex/hex.dart';
import 'package:http/http.dart' as http;
part 'electrum_wallet.g.dart'; part 'electrum_wallet.g.dart';
@ -101,6 +103,8 @@ abstract class ElectrumWalletBase
); );
reaction((_) => syncStatus, _syncStatusReaction); reaction((_) => syncStatus, _syncStatusReaction);
sharedPrefs.complete(SharedPreferences.getInstance());
} }
static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network, static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network,
@ -137,6 +141,8 @@ abstract class ElectrumWalletBase
Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1)); Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1));
final EncryptionFileUtils encryptionFileUtils; final EncryptionFileUtils encryptionFileUtils;
@override
final String? passphrase; final String? passphrase;
@override @override
@ -194,6 +200,13 @@ abstract class ElectrumWalletBase
bool _isTryingToConnect = false; bool _isTryingToConnect = false;
Completer<SharedPreferences> sharedPrefs = Completer();
Future<bool> checkIfMempoolAPIIsEnabled() async {
bool isMempoolAPIEnabled = (await sharedPrefs.future).getBool("use_mempool_fee_api") ?? true;
return isMempoolAPIEnabled;
}
@action @action
Future<void> setSilentPaymentsScanning(bool active) async { Future<void> setSilentPaymentsScanning(bool active) async {
silentPaymentsScanningActive = active; silentPaymentsScanningActive = active;
@ -263,7 +276,6 @@ abstract class ElectrumWalletBase
Future<Isolate>? _isolate; Future<Isolate>? _isolate;
void Function(FlutterErrorDetails)? _onError; void Function(FlutterErrorDetails)? _onError;
Timer? _reconnectTimer;
Timer? _autoSaveTimer; Timer? _autoSaveTimer;
Timer? _updateFeeRateTimer; Timer? _updateFeeRateTimer;
static const int _autoSaveInterval = 1; static const int _autoSaveInterval = 1;
@ -416,6 +428,10 @@ abstract class ElectrumWalletBase
@override @override
Future<void> startSync() async { Future<void> startSync() async {
try { try {
if (syncStatus is SyncronizingSyncStatus) {
return;
}
syncStatus = SyncronizingSyncStatus(); syncStatus = SyncronizingSyncStatus();
if (hasSilentPaymentsScanning) { if (hasSilentPaymentsScanning) {
@ -446,6 +462,20 @@ abstract class ElectrumWalletBase
@action @action
Future<void> updateFeeRates() async { Future<void> updateFeeRates() async {
if (await checkIfMempoolAPIIsEnabled()) {
try {
final response =
await http.get(Uri.parse("http://mempool.cakewallet.com:8999/api/v1/fees/recommended"));
final result = json.decode(response.body) as Map<String, num>;
final slowFee = result['economyFee']?.toInt() ?? 0;
final mediumFee = result['hourFee']?.toInt() ?? 0;
final fastFee = result['fastestFee']?.toInt() ?? 0;
_feeRates = [slowFee, mediumFee, fastFee];
return;
} catch (_) {}
}
final feeRates = await electrumClient.feeRates(network: network); final feeRates = await electrumClient.feeRates(network: network);
if (feeRates != [0, 0, 0]) { if (feeRates != [0, 0, 0]) {
_feeRates = feeRates; _feeRates = feeRates;
@ -1059,6 +1089,8 @@ abstract class ElectrumWalletBase
}); });
} }
unspentCoins.removeWhere((utxo) => estimatedTx.utxos.any((e) => e.utxo.txHash == utxo.hash));
await updateBalance(); await updateBalance();
}); });
} catch (e) { } catch (e) {
@ -1453,7 +1485,6 @@ abstract class ElectrumWalletBase
// Create a list of available outputs // Create a list of available outputs
final outputs = <BitcoinOutput>[]; final outputs = <BitcoinOutput>[];
for (final out in bundle.originalTransaction.outputs) { for (final out in bundle.originalTransaction.outputs) {
// Check if the script contains OP_RETURN // Check if the script contains OP_RETURN
final script = out.scriptPubKey.script; final script = out.scriptPubKey.script;
if (script.contains('OP_RETURN') && memo == null) { if (script.contains('OP_RETURN') && memo == null) {
@ -2029,9 +2060,8 @@ abstract class ElectrumWalletBase
_isTryingToConnect = true; _isTryingToConnect = true;
_reconnectTimer?.cancel(); Timer(Duration(seconds: 5), () {
_reconnectTimer = Timer(Duration(seconds: 10), () { if (this.syncStatus is NotConnectedSyncStatus || this.syncStatus is LostConnectionSyncStatus) {
if (this.syncStatus is! SyncedSyncStatus && this.syncStatus is! SyncedTipSyncStatus) {
this.electrumClient.connectToUri( this.electrumClient.connectToUri(
node!.uri, node!.uri,
useSSL: node!.useSSL ?? false, useSSL: node!.useSSL ?? false,
@ -2056,13 +2086,42 @@ abstract class ElectrumWalletBase
tx.inputAddresses!.isEmpty || tx.inputAddresses!.isEmpty ||
tx.outputAddresses == null || tx.outputAddresses == null ||
tx.outputAddresses!.isEmpty) { tx.outputAddresses!.isEmpty) {
tx = ElectrumTransactionInfo.fromElectrumBundle( List<String> inputAddresses = [];
bundle, List<String> outputAddresses = [];
walletInfo.type,
network, for (int i = 0; i < bundle.originalTransaction.inputs.length; i++) {
addresses: addressesSet, final input = bundle.originalTransaction.inputs[i];
height: tx.height, final inputTransaction = bundle.ins[i];
); final vout = input.txIndex;
final outTransaction = inputTransaction.outputs[vout];
final address = addressFromOutputScript(outTransaction.scriptPubKey, network);
if (address.isNotEmpty) inputAddresses.add(address);
}
for (int i = 0; i < bundle.originalTransaction.outputs.length; i++) {
final out = bundle.originalTransaction.outputs[i];
final address = addressFromOutputScript(out.scriptPubKey, network);
if (address.isNotEmpty) outputAddresses.add(address);
// Check if the script contains OP_RETURN
final script = out.scriptPubKey.script;
if (script.contains('OP_RETURN')) {
final index = script.indexOf('OP_RETURN');
if (index + 1 <= script.length) {
try {
final opReturnData = script[index + 1].toString();
final decodedString = utf8.decode(HEX.decode(opReturnData));
outputAddresses.add('OP_RETURN:$decodedString');
} catch (_) {
outputAddresses.add('OP_RETURN:');
}
}
}
}
tx.inputAddresses = inputAddresses;
tx.outputAddresses = outputAddresses;
transactionHistory.addOne(tx); transactionHistory.addOne(tx);
} }

View file

@ -7,6 +7,7 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
@ -36,6 +37,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes, required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
String? addressPageType, String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
@ -51,6 +53,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: seedBytes, seedBytes: seedBytes,
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
passphrase: passphrase,
currency: CryptoCurrency.ltc) { currency: CryptoCurrency.ltc) {
walletAddresses = LitecoinWalletAddresses( walletAddresses = LitecoinWalletAddresses(
walletInfo, walletInfo,
@ -89,7 +92,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
break; break;
case DerivationType.electrum: case DerivationType.electrum:
default: default:
seedBytes = await mnemonicToSeedBytes(mnemonic); seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break; break;
} }
return LitecoinWallet( return LitecoinWallet(
@ -100,6 +103,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
passphrase: passphrase,
seedBytes: seedBytes, seedBytes: seedBytes,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
@ -143,6 +147,31 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
); );
} }
walletInfo.derivationInfo ??= DerivationInfo();
// set the default if not present:
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
Uint8List? seedBytes = null;
final mnemonic = keysData.mnemonic;
final passphrase = keysData.passphrase;
if (mnemonic != null) {
switch (walletInfo.derivationInfo?.derivationType) {
case DerivationType.bip39:
seedBytes = await bip39.mnemonicToSeed(
mnemonic,
passphrase: passphrase ?? "",
);
break;
case DerivationType.electrum:
default:
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
}
}
return LitecoinWallet( return LitecoinWallet(
mnemonic: keysData.mnemonic!, mnemonic: keysData.mnemonic!,
password: password, password: password,
@ -150,7 +179,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: snp?.addresses, initialAddresses: snp?.addresses,
initialBalance: snp?.balance, initialBalance: snp?.balance,
seedBytes: await mnemonicToSeedBytes(keysData.mnemonic!), seedBytes: seedBytes!,
passphrase: passphrase,
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp?.regularAddressIndex, initialRegularAddressIndex: snp?.regularAddressIndex,
initialChangeAddressIndex: snp?.changeAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex,

View file

@ -1,4 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
@ -30,8 +31,21 @@ class LitecoinWalletService extends WalletService<
@override @override
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
final String mnemonic;
switch ( credentials.walletInfo?.derivationInfo?.derivationType) {
case DerivationType.bip39:
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
mnemonic = await MnemonicBip39.generate(strength: strength);
break;
case DerivationType.electrum:
default:
mnemonic = await generateElectrumMnemonic();
break;
}
final wallet = await LitecoinWalletBase.create( final wallet = await LitecoinWalletBase.create(
mnemonic: await generateElectrumMnemonic(), mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
passphrase: credentials.passphrase, passphrase: credentials.passphrase,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,

View file

@ -350,6 +350,11 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client: frontend_server_client:
dependency: transitive dependency: transitive
description: description:
@ -736,6 +741,62 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.27.7" version: "0.27.7"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
url: "https://pub.dev"
source: hosted
version: "2.2.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
url: "https://pub.dev"
source: hosted
version: "2.5.2"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@ -944,4 +1005,4 @@ packages:
version: "2.2.1" version: "2.2.1"
sdks: sdks:
dart: ">=3.3.0 <4.0.0" dart: ">=3.3.0 <4.0.0"
flutter: ">=3.16.6" flutter: ">=3.19.0"

View file

@ -17,6 +17,7 @@ dependencies:
mobx: ^2.0.7+4 mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1 flutter_mobx: ^2.0.6+1
intl: ^0.18.0 intl: ^0.18.0
shared_preferences: ^2.0.15
cw_core: cw_core:
path: ../cw_core path: ../cw_core
bitbox: bitbox:

View file

@ -3,5 +3,4 @@ export 'bitcoin_cash_wallet_addresses.dart';
export 'bitcoin_cash_wallet_creation_credentials.dart'; export 'bitcoin_cash_wallet_creation_credentials.dart';
export 'bitcoin_cash_wallet_service.dart'; export 'bitcoin_cash_wallet_service.dart';
export 'exceptions/exceptions.dart'; export 'exceptions/exceptions.dart';
export 'mnemonic.dart';
export 'bitcoin_cash_address_utils.dart'; export 'bitcoin_cash_address_utils.dart';

View file

@ -1,13 +1,14 @@
import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
@ -30,6 +31,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required Uint8List seedBytes, required Uint8List seedBytes,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
BitcoinAddressType? addressPageType, BitcoinAddressType? addressPageType,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
@ -45,7 +47,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: seedBytes, seedBytes: seedBytes,
currency: CryptoCurrency.bch, currency: CryptoCurrency.bch,
encryptionFileUtils: encryptionFileUtils) { encryptionFileUtils: encryptionFileUtils,
passphrase: passphrase) {
walletAddresses = BitcoinCashWalletAddresses( walletAddresses = BitcoinCashWalletAddresses(
walletInfo, walletInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
@ -67,6 +70,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
required WalletInfo walletInfo, required WalletInfo walletInfo,
required Box<UnspentCoinsInfo> unspentCoinsInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo,
required EncryptionFileUtils encryptionFileUtils, required EncryptionFileUtils encryptionFileUtils,
String? passphrase,
String? addressPageType, String? addressPageType,
List<BitcoinAddressRecord>? initialAddresses, List<BitcoinAddressRecord>? initialAddresses,
ElectrumBalance? initialBalance, ElectrumBalance? initialBalance,
@ -79,11 +83,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
unspentCoinsInfo: unspentCoinsInfo, unspentCoinsInfo: unspentCoinsInfo,
initialAddresses: initialAddresses, initialAddresses: initialAddresses,
initialBalance: initialBalance, initialBalance: initialBalance,
seedBytes: await MnemonicBip39.toSeed(mnemonic), seedBytes: await MnemonicBip39.toSeed(mnemonic, passphrase: passphrase),
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: initialRegularAddressIndex, initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh, addressPageType: P2pkhAddressType.p2pkh,
passphrase: passphrase,
); );
} }
@ -150,11 +155,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
} }
}).toList(), }).toList(),
initialBalance: snp?.balance, initialBalance: snp?.balance,
seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!), seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!, passphrase: keysData.passphrase),
encryptionFileUtils: encryptionFileUtils, encryptionFileUtils: encryptionFileUtils,
initialRegularAddressIndex: snp?.regularAddressIndex, initialRegularAddressIndex: snp?.regularAddressIndex,
initialChangeAddressIndex: snp?.changeAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex,
addressPageType: P2pkhAddressType.p2pkh, addressPageType: P2pkhAddressType.p2pkh,
passphrase: keysData.passphrase,
); );
} }

View file

@ -2,17 +2,19 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
class BitcoinCashNewWalletCredentials extends WalletCredentials { class BitcoinCashNewWalletCredentials extends WalletCredentials {
BitcoinCashNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) BitcoinCashNewWalletCredentials(
: super(name: name, walletInfo: walletInfo, password: password); {required String name, WalletInfo? walletInfo, String? password, String? passphrase})
: super(name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
} }
class BitcoinCashRestoreWalletFromSeedCredentials extends WalletCredentials { class BitcoinCashRestoreWalletFromSeedCredentials extends WalletCredentials {
BitcoinCashRestoreWalletFromSeedCredentials( BitcoinCashRestoreWalletFromSeedCredentials({
{required String name, required String name,
required String password, required String password,
required this.mnemonic, required this.mnemonic,
WalletInfo? walletInfo}) WalletInfo? walletInfo,
: super(name: name, password: password, walletInfo: walletInfo); String? passphrase,
}) : super(name: name, password: password, walletInfo: walletInfo, passphrase: passphrase);
final String mnemonic; final String mnemonic;
} }

View file

@ -1,6 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'package:bip39/bip39.dart'; import 'package:bip39/bip39.dart';
import 'package:collection/collection.dart';
import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart';
import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart'; import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart';
import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
@ -9,7 +11,6 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:collection/collection.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
class BitcoinCashWalletService extends WalletService< class BitcoinCashWalletService extends WalletService<
@ -35,11 +36,12 @@ class BitcoinCashWalletService extends WalletService<
final strength = credentials.seedPhraseLength == 24 ? 256 : 128; final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
final wallet = await BitcoinCashWalletBase.create( final wallet = await BitcoinCashWalletBase.create(
mnemonic: await MnemonicBip39.generate(strength: strength), mnemonic: await MnemonicBip39.generate(strength: strength),
password: credentials.password!, password: credentials.password!,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
passphrase: credentials.passphrase,
); );
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
@ -54,11 +56,11 @@ class BitcoinCashWalletService extends WalletService<
try { try {
final wallet = await BitcoinCashWalletBase.open( final wallet = await BitcoinCashWalletBase.open(
password: password, password: password,
name: name, name: name,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
await wallet.init(); await wallet.init();
saveBackup(name); saveBackup(name);
@ -66,11 +68,11 @@ class BitcoinCashWalletService extends WalletService<
} catch (_) { } catch (_) {
await restoreWalletFilesFromBackup(name); await restoreWalletFilesFromBackup(name);
final wallet = await BitcoinCashWalletBase.open( final wallet = await BitcoinCashWalletBase.open(
password: password, password: password,
name: name, name: name,
walletInfo: walletInfo, walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect), encryptionFileUtils: encryptionFileUtilsFor(isDirect),
); );
await wallet.init(); await wallet.init();
return wallet; return wallet;
@ -130,7 +132,9 @@ class BitcoinCashWalletService extends WalletService<
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
unspentCoinsInfo: unspentCoinsInfoSource, unspentCoinsInfo: unspentCoinsInfoSource,
encryptionFileUtils: encryptionFileUtilsFor(isDirect)); encryptionFileUtils: encryptionFileUtilsFor(isDirect),
passphrase: credentials.passphrase
);
await wallet.save(); await wallet.save();
await wallet.init(); await wallet.init();
return wallet; return wallet;

View file

@ -46,6 +46,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
String? get hexSeed => null; String? get hexSeed => null;
String? get passphrase => null;
Object get keys; Object get keys;
WalletAddresses get walletAddresses; WalletAddresses get walletAddresses;

View file

@ -138,11 +138,17 @@ PendingTransactionDescription createTransactionMultDestSync(
int accountIndex = 0, int accountIndex = 0,
List<String> preferredInputs = const []}) { List<String> preferredInputs = const []}) {
final dstAddrs = outputs.map((e) => e.address).toList();
final amounts = outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList();
// print("multDest: dstAddrs: $dstAddrs");
// print("multDest: amounts: $amounts");
final txptr = monero.Wallet_createTransactionMultDest( final txptr = monero.Wallet_createTransactionMultDest(
wptr!, wptr!,
dstAddr: outputs.map((e) => e.address).toList(), dstAddr: dstAddrs,
isSweepAll: false, isSweepAll: false,
amounts: outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(), amounts: amounts,
mixinCount: 0, mixinCount: 0,
pendingTransactionPriority: priorityRaw, pendingTransactionPriority: priorityRaw,
subaddr_account: accountIndex, subaddr_account: accountIndex,
@ -307,7 +313,34 @@ class Transaction {
confirmations = monero.TransactionInfo_confirmations(txInfo), confirmations = monero.TransactionInfo_confirmations(txInfo),
fee = monero.TransactionInfo_fee(txInfo), fee = monero.TransactionInfo_fee(txInfo),
description = monero.TransactionInfo_description(txInfo), description = monero.TransactionInfo_description(txInfo),
key = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo)); key = getTxKey(txInfo);
static String getTxKey(monero.TransactionInfo txInfo) {
final txKey = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo));
final status = monero.Wallet_status(wptr!);
if (status != 0) {
return monero.Wallet_errorString(wptr!);
}
return breakTxKey(txKey);
}
static String breakTxKey(String input) {
final x = 64;
StringBuffer buffer = StringBuffer();
for (int i = 0; i < input.length; i += x) {
int endIndex = i + x;
if (endIndex > input.length) {
endIndex = input.length;
}
buffer.write(input.substring(i, endIndex));
if (endIndex != input.length) {
buffer.write('\n\n');
}
}
return buffer.toString().trim();
}
Transaction.dummy({ Transaction.dummy({
required this.displayLabel, required this.displayLabel,

View file

@ -13,11 +13,9 @@ import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/monero_wallet_keys.dart'; import 'package:cw_core/monero_wallet_keys.dart';
import 'package:cw_core/monero_wallet_utils.dart'; import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart'; import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';

View file

@ -9,11 +9,9 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/get_height_by_date.dart'; import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager; import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
import 'package:cw_monero/api/wallet_manager.dart'; import 'package:cw_monero/api/wallet_manager.dart';
import 'package:cw_monero/monero_wallet.dart'; import 'package:cw_monero/monero_wallet.dart';
import 'package:flutter/widgets.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:polyseed/polyseed.dart'; import 'package:polyseed/polyseed.dart';
import 'package:monero/monero.dart' as monero; import 'package:monero/monero.dart' as monero;
@ -120,7 +118,6 @@ class MoneroWalletService extends WalletService<
@override @override
Future<MoneroWallet> openWallet(String name, String password, {bool? retryOnFailure}) async { Future<MoneroWallet> openWallet(String name, String password, {bool? retryOnFailure}) async {
MoneroWallet? wallet;
try { try {
final path = await pathForWallet(name: name, type: getType()); final path = await pathForWallet(name: name, type: getType());
@ -147,41 +144,10 @@ class MoneroWalletService extends WalletService<
await wallet.init(); await wallet.init();
return wallet; return wallet;
} catch (e, s) { } catch (e) {
// TODO: Implement Exception for wallet list service. // TODO: Implement Exception for wallet list service.
final bool isBadAlloc = e.toString().contains('bad_alloc') || if (retryOnFailure == false) {
(e is WalletOpeningException &&
(e.message == 'std::bad_alloc' || e.message.contains('bad_alloc')));
final bool doesNotCorrespond = e.toString().contains('does not correspond') ||
(e is WalletOpeningException && e.message.contains('does not correspond'));
final bool isMissingCacheFilesIOS = e.toString().contains('basic_string') ||
(e is WalletOpeningException && e.message.contains('basic_string'));
final bool isMissingCacheFilesAndroid = e.toString().contains('input_stream') ||
e.toString().contains('input stream error') ||
(e is WalletOpeningException &&
(e.message.contains('input_stream') || e.message.contains('input stream error')));
final bool invalidSignature = e.toString().contains('invalid signature') ||
(e is WalletOpeningException && e.message.contains('invalid signature'));
final bool invalidPassword = e.toString().contains('invalid password') ||
(e is WalletOpeningException && e.message.contains('invalid password'));
if (!isBadAlloc &&
!doesNotCorrespond &&
!isMissingCacheFilesIOS &&
!isMissingCacheFilesAndroid &&
!invalidSignature &&
!invalidPassword &&
wallet != null &&
wallet.onError != null) {
wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
}
if (invalidPassword || retryOnFailure == false) {
rethrow; rethrow;
} }

View file

@ -463,8 +463,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "impls/monero.dart" path: "impls/monero.dart"
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b resolved-ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
url: "https://github.com/mrcyjanek/monero_c" url: "https://github.com/mrcyjanek/monero_c"
source: git source: git
version: "0.0.0" version: "0.0.0"

View file

@ -25,7 +25,7 @@ dependencies:
monero: monero:
git: git:
url: https://github.com/mrcyjanek/monero_c url: https://github.com/mrcyjanek/monero_c
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
path: impls/monero.dart path: impls/monero.dart
mutex: ^3.1.0 mutex: ^3.1.0

View file

@ -466,21 +466,25 @@ class NanoClient {
blocks = blocks as Map<String, dynamic>; blocks = blocks as Map<String, dynamic>;
// confirm all receivable blocks: try {
for (final blockHash in blocks.keys) { // confirm all receivable blocks:
final block = blocks[blockHash]; for (final blockHash in blocks.keys) {
final String amountRaw = block["amount"] as String; final block = blocks[blockHash];
await receiveBlock( final String amountRaw = block["amount"] as String;
blockHash: blockHash, await receiveBlock(
amountRaw: amountRaw, blockHash: blockHash,
privateKey: privateKey, amountRaw: amountRaw,
destinationAddress: destinationAddress, privateKey: privateKey,
); destinationAddress: destinationAddress,
// a bit of a hack: );
await Future<void>.delayed(const Duration(seconds: 2)); // a bit of a hack:
await Future<void>.delayed(const Duration(seconds: 2));
}
return blocks.keys.length;
} catch (_) {
// we failed to confirm all receivable blocks for w/e reason (PoW / node outage / etc)
return 0;
} }
return blocks.keys.length;
} }
void stop() {} void stop() {}

View file

@ -14,8 +14,11 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:nanodart/nanodart.dart'; import 'package:nanodart/nanodart.dart';
import 'package:nanoutil/nanoutil.dart'; import 'package:nanoutil/nanoutil.dart';
class NanoWalletService extends WalletService<NanoNewWalletCredentials, class NanoWalletService extends WalletService<
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> { NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials,
NanoRestoreWalletFromKeysCredentials,
NanoNewWalletCredentials> {
NanoWalletService(this.walletInfoSource, this.isDirect); NanoWalletService(this.walletInfoSource, this.isDirect);
final Box<WalletInfo> walletInfoSource; final Box<WalletInfo> walletInfoSource;
@ -33,8 +36,12 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
String seedKey = NanoSeeds.generateSeed(); String seedKey = NanoSeeds.generateSeed();
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey); String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
// ensure default if not present: // should never happen but just in case:
credentials.walletInfo!.derivationInfo ??= DerivationInfo(derivationType: DerivationType.nano); if (credentials.walletInfo!.derivationInfo == null) {
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
}
final wallet = NanoWallet( final wallet = NanoWallet(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
@ -86,7 +93,8 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
} }
@override @override
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async { Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials,
{bool? isTestnet}) async {
if (credentials.seedKey.contains(' ')) { if (credentials.seedKey.contains(' ')) {
throw Exception("Invalid key!"); throw Exception("Invalid key!");
} else { } else {
@ -106,6 +114,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
} }
} }
// should never happen but just in case:
if (credentials.walletInfo!.derivationInfo == null) {
credentials.walletInfo!.derivationInfo = DerivationInfo(derivationType: DerivationType.nano);
} else if (credentials.walletInfo!.derivationInfo!.derivationType == null) {
credentials.walletInfo!.derivationInfo!.derivationType = DerivationType.nano;
}
final wallet = await NanoWallet( final wallet = await NanoWallet(
password: credentials.password!, password: credentials.password!,
mnemonic: mnemonic ?? credentials.seedKey, mnemonic: mnemonic ?? credentials.seedKey,
@ -119,11 +134,13 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
@override @override
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) { Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
throw UnimplementedError("Restoring a Nano wallet from a hardware wallet is not yet supported!"); throw UnimplementedError(
"Restoring a Nano wallet from a hardware wallet is not yet supported!");
} }
@override @override
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
if (credentials.mnemonic.contains(' ')) { if (credentials.mnemonic.contains(' ')) {
if (!bip39.validateMnemonic(credentials.mnemonic)) { if (!bip39.validateMnemonic(credentials.mnemonic)) {
throw nm.NanoMnemonicIsIncorrectException(); throw nm.NanoMnemonicIsIncorrectException();

View file

@ -463,8 +463,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "impls/monero.dart" path: "impls/monero.dart"
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
resolved-ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b resolved-ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7
url: "https://github.com/mrcyjanek/monero_c" url: "https://github.com/mrcyjanek/monero_c"
source: git source: git
version: "0.0.0" version: "0.0.0"

View file

@ -25,7 +25,7 @@ dependencies:
monero: monero:
git: git:
url: https://github.com/mrcyjanek/monero_c url: https://github.com/mrcyjanek/monero_c
ref: bcb328a4956105dc182afd0ce2e48fe263f5f20b # monero_c hash ref: 3cb38bee9385faf46b03fd73aab85f3ac4115bf7 # monero_c hash
path: impls/monero.dart path: impls/monero.dart
mutex: ^3.1.0 mutex: ^3.1.0

View file

@ -29,8 +29,9 @@ class CWBitcoin extends Bitcoin {
@override @override
WalletCredentials createBitcoinNewWalletCredentials( WalletCredentials createBitcoinNewWalletCredentials(
{required String name, WalletInfo? walletInfo, String? password}) => {required String name, WalletInfo? walletInfo, String? password, String? passphrase}) =>
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); BitcoinNewWalletCredentials(
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
@override @override
WalletCredentials createBitcoinHardwareWalletCredentials( WalletCredentials createBitcoinHardwareWalletCredentials(
@ -202,8 +203,8 @@ class CWBitcoin extends Bitcoin {
await bitcoinWallet.updateAllUnspents(); await bitcoinWallet.updateAllUnspents();
} }
WalletService createBitcoinWalletService( WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource,
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) { Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) {
return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect); return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect);
} }
@ -315,7 +316,7 @@ class CWBitcoin extends Bitcoin {
for (DerivationType dType in electrum_derivations.keys) { for (DerivationType dType in electrum_derivations.keys) {
late Uint8List seedBytes; late Uint8List seedBytes;
if (dType == DerivationType.electrum) { if (dType == DerivationType.electrum) {
seedBytes = await mnemonicToSeedBytes(mnemonic); seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
} else if (dType == DerivationType.bip39) { } else if (dType == DerivationType.bip39) {
seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? ''); seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
} }

View file

@ -15,14 +15,16 @@ class CWBitcoinCash extends BitcoinCash {
required String name, required String name,
WalletInfo? walletInfo, WalletInfo? walletInfo,
String? password, String? password,
String? passphrase,
}) => }) =>
BitcoinCashNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); BitcoinCashNewWalletCredentials(
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
@override @override
WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials( WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials(
{required String name, required String mnemonic, required String password}) => {required String name, required String mnemonic, required String password, String? passphrase}) =>
BitcoinCashRestoreWalletFromSeedCredentials( BitcoinCashRestoreWalletFromSeedCredentials(
name: name, mnemonic: mnemonic, password: password); name: name, mnemonic: mnemonic, password: password, passphrase: passphrase);
@override @override
TransactionPriority deserializeBitcoinCashTransactionPriority(int raw) => TransactionPriority deserializeBitcoinCashTransactionPriority(int raw) =>

View file

@ -75,8 +75,10 @@ class WalletCreationService {
bool get _hasSeedPhraseLengthOption { bool get _hasSeedPhraseLengthOption {
switch (type) { switch (type) {
case WalletType.ethereum: case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
case WalletType.ethereum:
case WalletType.polygon: case WalletType.polygon:
case WalletType.solana: case WalletType.solana:
case WalletType.tron: case WalletType.tron:
@ -84,8 +86,6 @@ class WalletCreationService {
case WalletType.monero: case WalletType.monero:
case WalletType.wownero: case WalletType.wownero:
case WalletType.none: case WalletType.none:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.haven: case WalletType.haven:
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:

View file

@ -146,7 +146,7 @@ import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dar
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart'; import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
@ -179,6 +179,7 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart'; import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/store/node_list_store.dart'; import 'package:cake_wallet/store/node_list_store.dart';
import 'package:cake_wallet/store/secret_store.dart'; import 'package:cake_wallet/store/secret_store.dart';
import 'package:cake_wallet/store/seed_settings_store.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/templates/exchange_template_store.dart'; import 'package:cake_wallet/store/templates/exchange_template_store.dart';
import 'package:cake_wallet/store/templates/send_template_store.dart'; import 'package:cake_wallet/store/templates/send_template_store.dart';
@ -331,6 +332,7 @@ Future<void> setup({
YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>())..init()); YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>())..init());
getIt.registerSingleton<AnonpayTransactionsStore>( getIt.registerSingleton<AnonpayTransactionsStore>(
AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource));
getIt.registerSingleton<SeedSettingsStore>(SeedSettingsStore());
getIt.registerLazySingleton(() => LedgerViewModel()); getIt.registerLazySingleton(() => LedgerViewModel());
@ -361,6 +363,7 @@ Future<void> setup({
getIt.get<WalletCreationService>(param1: type), getIt.get<WalletCreationService>(param1: type),
_walletInfoSource, _walletInfoSource,
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type), getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
getIt.get<SeedSettingsViewModel>(),
type: type)); type: type));
getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) { getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) {
@ -422,14 +425,21 @@ Future<void> setup({
walletType: args.walletType ?? currentWalletType); walletType: args.walletType ?? currentWalletType);
}); });
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) { getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) =>
return WalletRestorationFromQRVM(getIt.get<AppStore>(), WalletRestorationFromQRVM(
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type); getIt.get<AppStore>(),
}); getIt.get<WalletCreationService>(param1: type),
_walletInfoSource,
type,
getIt.get<SeedSettingsViewModel>()));
getIt.registerFactoryParam<WalletHardwareRestoreViewModel, WalletType, void>((type, _) => getIt.registerFactoryParam<WalletHardwareRestoreViewModel, WalletType, void>((type, _) =>
WalletHardwareRestoreViewModel(getIt.get<LedgerViewModel>(), getIt.get<AppStore>(), WalletHardwareRestoreViewModel(
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, getIt.get<LedgerViewModel>(),
getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type),
_walletInfoSource,
getIt.get<SeedSettingsViewModel>(),
type: type)); type: type));
getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel( getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel(
@ -833,7 +843,7 @@ Future<void> setup({
getIt.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!)); getIt.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!));
getIt.registerFactory<SeedTypeViewModel>(() => SeedTypeViewModel(getIt.get<AppStore>())); getIt.registerFactory<SeedSettingsViewModel>(() => SeedSettingsViewModel(getIt.get<AppStore>(), getIt.get<SeedSettingsStore>()));
getIt.registerFactoryParam<WalletSeedPage, bool, void>((bool isWalletCreated, _) => getIt.registerFactoryParam<WalletSeedPage, bool, void>((bool isWalletCreated, _) =>
WalletSeedPage(getIt.get<WalletSeedViewModel>(), isNewWalletCreated: isWalletCreated)); WalletSeedPage(getIt.get<WalletSeedViewModel>(), isNewWalletCreated: isWalletCreated));
@ -1018,12 +1028,12 @@ Future<void> setup({
getIt.registerFactory(() => FaqPage(getIt.get<SettingsStore>())); getIt.registerFactory(() => FaqPage(getIt.get<SettingsStore>()));
getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>((type, _) => getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>((type, _) =>
WalletRestoreViewModel( WalletRestoreViewModel(getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type),
getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource, _walletInfoSource, getIt.get<SeedSettingsViewModel>(),
type: type)); type: type));
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) => WalletRestorePage( getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) => WalletRestorePage(
getIt.get<WalletRestoreViewModel>(param1: type), getIt.get<SeedTypeViewModel>())); getIt.get<WalletRestoreViewModel>(param1: type), getIt.get<SeedSettingsViewModel>()));
getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, List<DerivationInfo>, void>( getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, List<DerivationInfo>, void>(
(derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations));

View file

@ -40,6 +40,7 @@ const solanaDefaultNodeUri = 'rpc.ankr.com';
const tronDefaultNodeUri = 'trx.nownodes.io'; const tronDefaultNodeUri = 'trx.nownodes.io';
const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002'; const newCakeWalletBitcoinUri = 'btc-electrum.cakewallet.com:50002';
const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568'; const wowneroDefaultNodeUri = 'node3.monerodevs.org:34568';
const moneroWorldNodeUri = '.moneroworld.com';
Future<void> defaultSettingsMigration( Future<void> defaultSettingsMigration(
{required int version, {required int version,
@ -245,6 +246,9 @@ Future<void> defaultSettingsMigration(
_fixNodesUseSSLFlag(nodes); _fixNodesUseSSLFlag(nodes);
await changeDefaultNanoNode(nodes, sharedPreferences); await changeDefaultNanoNode(nodes, sharedPreferences);
break; break;
case 40:
await removeMoneroWorld(sharedPreferences: sharedPreferences, nodes: nodes);
break;
default: default:
break; break;
} }
@ -488,15 +492,7 @@ Node? getBitcoinCashDefaultElectrumServer({required Box<Node> nodes}) {
Node getMoneroDefaultNode({required Box<Node> nodes}) { Node getMoneroDefaultNode({required Box<Node> nodes}) {
final timeZone = DateTime.now().timeZoneOffset.inHours; final timeZone = DateTime.now().timeZoneOffset.inHours;
var nodeUri = ''; var nodeUri = newCakeWalletMoneroUri;
if (timeZone >= 1) {
// Eurasia
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
} else if (timeZone <= -4) {
// America
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
}
try { try {
return nodes.values.firstWhere((Node node) => node.uriRaw == nodeUri); return nodes.values.firstWhere((Node node) => node.uriRaw == nodeUri);
@ -1260,3 +1256,22 @@ Future<void> replaceTronDefaultNode({
// If it's not, we switch user to the new default node: NowNodes // If it's not, we switch user to the new default node: NowNodes
await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes); await changeTronCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
} }
Future<void> removeMoneroWorld(
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
const cakeWalletMoneroNodeUriPattern = '.moneroworld.com';
final currentMoneroNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
final currentMoneroNode = nodes.values.firstWhere((node) => node.key == currentMoneroNodeId);
final needToReplaceCurrentMoneroNode = currentMoneroNode.uri.toString().contains(cakeWalletMoneroNodeUriPattern);
nodes.values.forEach((node) async {
if (node.type == WalletType.monero &&
node.uri.toString().contains(cakeWalletMoneroNodeUriPattern)) {
await node.delete();
}
});
if (needToReplaceCurrentMoneroNode) {
await changeMoneroCurrentNodeToDefault(sharedPreferences: sharedPreferences, nodes: nodes);
}
}

View file

@ -60,7 +60,7 @@ class LanguageService {
'yo': 'nga', 'yo': 'nga',
'ha': 'hau', 'ha': 'hau',
'tl': 'phl', 'tl': 'phl',
'hy': 'arm' 'hy': 'arm',
}; };
static final list = <String, String>{}; static final list = <String, String>{};

View file

@ -61,6 +61,7 @@ class PreferencesKey {
static const useEtherscan = 'use_etherscan'; static const useEtherscan = 'use_etherscan';
static const usePolygonScan = 'use_polygonscan'; static const usePolygonScan = 'use_polygonscan';
static const useTronGrid = 'use_trongrid'; static const useTronGrid = 'use_trongrid';
static const useMempoolFeeAPI = 'use_mempool_fee_api';
static const defaultNanoRep = 'default_nano_representative'; static const defaultNanoRep = 'default_nano_representative';
static const defaultBananoRep = 'default_banano_representative'; static const defaultBananoRep = 'default_banano_representative';
static const lookupsTwitter = 'looks_up_twitter'; static const lookupsTwitter = 'looks_up_twitter';
@ -77,6 +78,7 @@ class PreferencesKey {
static const exchangeProvidersSelection = 'exchange-providers-selection'; static const exchangeProvidersSelection = 'exchange-providers-selection';
static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status'; static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status';
static const moneroSeedType = 'monero_seed_type'; static const moneroSeedType = 'monero_seed_type';
static const bitcoinSeedType = 'bitcoin_seed_type';
static const clearnetDonationLink = 'clearnet_donation_link'; static const clearnetDonationLink = 'clearnet_donation_link';
static const onionDonationLink = 'onion_donation_link'; static const onionDonationLink = 'onion_donation_link';
static const donationLinkWalletName = 'donation_link_wallet_name'; static const donationLinkWalletName = 'donation_link_wallet_name';

View file

@ -1,18 +1,19 @@
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/enumerable_item.dart'; import 'package:cw_core/enumerable_item.dart';
import 'package:cw_core/wallet_info.dart';
class SeedType extends EnumerableItem<int> with Serializable<int> { class MoneroSeedType extends EnumerableItem<int> with Serializable<int> {
const SeedType({required String title, required int raw}) : super(title: title, raw: raw); const MoneroSeedType({required String title, required int raw}) : super(title: title, raw: raw);
static const all = [SeedType.legacy, SeedType.polyseed]; static const all = [MoneroSeedType.legacy, MoneroSeedType.polyseed];
static const defaultSeedType = polyseed; static const defaultSeedType = polyseed;
static const legacy = SeedType(raw: 0, title: 'Legacy (25 words)'); static const legacy = MoneroSeedType(raw: 0, title: 'Legacy (25 words)');
static const polyseed = SeedType(raw: 1, title: 'Polyseed (16 words)'); static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed (16 words)');
static const wowneroSeed = SeedType(raw: 2, title: 'Wownero (14 words)'); static const wowneroSeed = MoneroSeedType(raw: 2, title: 'Wownero (14 words)');
static SeedType deserialize({required int raw}) { static MoneroSeedType deserialize({required int raw}) {
switch (raw) { switch (raw) {
case 0: case 0:
return legacy; return legacy;
@ -28,14 +29,39 @@ class SeedType extends EnumerableItem<int> with Serializable<int> {
@override @override
String toString() { String toString() {
switch (this) { switch (this) {
case SeedType.legacy: case MoneroSeedType.legacy:
return S.current.seedtype_legacy; return S.current.seedtype_legacy;
case SeedType.polyseed: case MoneroSeedType.polyseed:
return S.current.seedtype_polyseed; return S.current.seedtype_polyseed;
case SeedType.wowneroSeed: case MoneroSeedType.wowneroSeed:
return S.current.seedtype_wownero; return S.current.seedtype_wownero;
default: default:
return ''; return '';
} }
} }
} }
class BitcoinSeedType extends EnumerableItem<int> with Serializable<int> {
const BitcoinSeedType(this.type, {required String title, required int raw})
: super(title: title, raw: raw);
final DerivationType type;
static const all = [BitcoinSeedType.electrum, BitcoinSeedType.bip39];
static const defaultDerivationType = bip39;
static const electrum = BitcoinSeedType(DerivationType.electrum, raw: 0, title: 'Electrum');
static const bip39 = BitcoinSeedType(DerivationType.bip39, raw: 1, title: 'BIP39');
static BitcoinSeedType deserialize({required int raw}) {
switch (raw) {
case 0:
return electrum;
case 1:
return bip39;
default:
throw Exception('Unexpected token: $raw for SeedType deserialize');
}
}
}

View file

@ -27,6 +27,10 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
ExchangeProviderDescription(title: 'ThorChain', raw: 8, image: 'assets/images/thorchain.png'); ExchangeProviderDescription(title: 'ThorChain', raw: 8, image: 'assets/images/thorchain.png');
static const quantex = static const quantex =
ExchangeProviderDescription(title: 'Quantex', raw: 9, image: 'assets/images/quantex.png'); ExchangeProviderDescription(title: 'Quantex', raw: 9, image: 'assets/images/quantex.png');
static const letsExchange =
ExchangeProviderDescription(title: 'LetsExchange', raw: 10, image: 'assets/images/letsexchange_icon.svg');
static const stealthEx =
ExchangeProviderDescription(title: 'StealthEx', raw: 11, image: 'assets/images/stealthex.png');
static ExchangeProviderDescription deserialize({required int raw}) { static ExchangeProviderDescription deserialize({required int raw}) {
switch (raw) { switch (raw) {
@ -50,6 +54,10 @@ class ExchangeProviderDescription extends EnumerableItem<int> with Serializable<
return thorChain; return thorChain;
case 9: case 9:
return quantex; return quantex;
case 10:
return letsExchange;
case 11:
return stealthEx;
default: default:
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
} }

View file

@ -0,0 +1,292 @@
import 'dart:convert';
import 'dart:developer';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart' as http;
class LetsExchangeExchangeProvider extends ExchangeProvider {
LetsExchangeExchangeProvider() : super(pairList: supportedPairs(_notSupported));
static const List<CryptoCurrency> _notSupported = [];
static const apiKey = secrets.letsExchangeBearerToken;
static const _baseUrl = 'api.letsexchange.io';
static const _infoPath = '/api/v1/info';
static const _infoRevertPath = '/api/v1/info-revert';
static const _createTransactionPath = '/api/v1/transaction';
static const _createTransactionRevertPath = '/api/v1/transaction-revert';
static const _getTransactionPath = '/api/v1/transaction';
static const _affiliateId = secrets.letsExchangeAffiliateId;
@override
String get title => 'LetsExchange';
@override
bool get isAvailable => true;
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
ExchangeProviderDescription get description => ExchangeProviderDescription.letsExchange;
@override
Future<bool> checkIsAvailable() async => true;
@override
Future<Limits> fetchLimits(
{required CryptoCurrency from,
required CryptoCurrency to,
required bool isFixedRateMode}) async {
final networkFrom = _getNetworkType(from);
final networkTo = _getNetworkType(to);
try {
final params = {
'from': from.title,
'to': to.title,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': '1',
'affiliate_id': _affiliateId
};
final responseJSON = await _getInfo(params, isFixedRateMode);
final min = double.tryParse(responseJSON['min_amount'] as String);
final max = double.tryParse(responseJSON['max_amount'] as String);
return Limits(min: min, max: max);
} catch (e) {
log(e.toString());
throw Exception('Failed to fetch limits');
}
}
@override
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode,
required bool isReceiveAmount}) async {
final networkFrom = _getNetworkType(from);
final networkTo = _getNetworkType(to);
try {
final params = {
'from': from.title,
'to': to.title,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': amount.toString(),
'affiliate_id': _affiliateId
};
final responseJSON = await _getInfo(params, isFixedRateMode);
final amountToGet = double.tryParse(responseJSON['amount'] as String) ?? 0.0;
return isFixedRateMode ? amount / amountToGet : amountToGet / amount;
} catch (e) {
log(e.toString());
return 0.0;
}
}
@override
Future<Trade> createTrade(
{required TradeRequest request,
required bool isFixedRateMode,
required bool isSendAll}) async {
final networkFrom = _getNetworkType(request.fromCurrency);
final networkTo = _getNetworkType(request.toCurrency);
try {
final params = {
'from': request.fromCurrency.title,
'to': request.toCurrency.title,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'amount': isFixedRateMode ? request.toAmount.toString() : request.fromAmount.toString(),
'affiliate_id': _affiliateId
};
final responseInfoJSON = await _getInfo(params, isFixedRateMode);
final rateId = responseInfoJSON['rate_id'] as String;
final withdrawalAddress = _normalizeBchAddress(request.toAddress);
final returnAddress = _normalizeBchAddress(request.refundAddress);
final tradeParams = {
'coin_from': request.fromCurrency.title,
'coin_to': request.toCurrency.title,
if (!isFixedRateMode) 'deposit_amount': request.fromAmount.toString(),
'withdrawal': withdrawalAddress,
if (isFixedRateMode) 'withdrawal_amount': request.toAmount.toString(),
'withdrawal_extra_id': '',
'return': returnAddress,
'rate_id': rateId,
if (networkFrom != null) 'network_from': networkFrom,
if (networkTo != null) 'network_to': networkTo,
'affiliate_id': _affiliateId
};
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': apiKey
};
final uri = Uri.https(_baseUrl,
isFixedRateMode ? _createTransactionRevertPath : _createTransactionPath, tradeParams);
final response = await http.post(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('LetsExchange create trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['transaction_id'] as String;
final from = responseJSON['coin_from'] as String;
final to = responseJSON['coin_to'] as String;
final payoutAddress = responseJSON['withdrawal'] as String;
final depositAddress = responseJSON['deposit'] as String;
final refundAddress = responseJSON['return'] as String;
final depositAmount = responseJSON['deposit_amount'] as String;
final receiveAmount = responseJSON['withdrawal_amount'] as String;
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final expiredAtTimestamp = responseJSON['expired_at'] as int;
final createdAt = DateTime.parse(createdAtString);
final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000);
CryptoCurrency fromCurrency;
if (request.fromCurrency.tag != null && request.fromCurrency.title == from) {
fromCurrency = request.fromCurrency;
} else {
fromCurrency = CryptoCurrency.fromString(from);
}
CryptoCurrency toCurrency;
if (request.toCurrency.tag != null && request.toCurrency.title == to) {
toCurrency = request.toCurrency;
} else {
toCurrency = CryptoCurrency.fromString(to);
}
return Trade(
id: id,
from: fromCurrency,
to: toCurrency,
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount,
receiveAmount: receiveAmount,
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
expiredAt: expiredAt,
);
} catch (e) {
log(e.toString());
throw TradeNotCreatedException(description);
}
}
@override
Future<Trade> findTradeById({required String id}) async {
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': apiKey
};
final url = Uri.https(_baseUrl, '$_getTransactionPath/$id');
final response = await http.get(url, headers: headers);
if (response.statusCode != 200) {
throw Exception('LetsExchange fetch trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final from = responseJSON['coin_from'] as String;
final to = responseJSON['coin_to'] as String;
final payoutAddress = responseJSON['withdrawal'] as String;
final depositAddress = responseJSON['deposit'] as String;
final refundAddress = responseJSON['return'] as String;
final depositAmount = responseJSON['deposit_amount'] as String;
final receiveAmount = responseJSON['withdrawal_amount'] as String;
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final expiredAtTimestamp = responseJSON['expired_at'] as int;
final createdAt = DateTime.parse(createdAtString);
final expiredAt = DateTime.fromMillisecondsSinceEpoch(expiredAtTimestamp * 1000);
return Trade(
id: id,
from: CryptoCurrency.fromString(from),
to: CryptoCurrency.fromString(to),
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount,
receiveAmount: receiveAmount,
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
expiredAt: expiredAt,
isRefund: status == 'refund',
);
}
Future<Map<String, dynamic>> _getInfo(Map<String, String> params, bool isFixedRateMode) async {
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': apiKey
};
try {
final uri = Uri.https(_baseUrl, isFixedRateMode ? _infoRevertPath : _infoPath, params);
final response = await http.post(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('LetsExchange fetch info failed: ${response.body}');
}
return json.decode(response.body) as Map<String, dynamic>;
} catch (e) {
throw Exception('LetsExchange failed to fetch info ${e.toString()}');
}
}
String? _getNetworkType(CryptoCurrency currency) {
if (currency.tag != null && currency.tag!.isNotEmpty) {
switch (currency.tag!) {
case 'TRX':
return 'TRC20';
case 'ETH':
return 'ERC20';
case 'BSC':
return 'BEP20';
case 'POLY':
return 'MATIC';
default:
return currency.tag!;
}
}
return currency.title;
}
String _normalizeBchAddress(String address) =>
address.startsWith('bitcoincash:') ? address.substring(12) : address;
}

View file

@ -0,0 +1,299 @@
import 'dart:convert';
import 'dart:developer';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/trade_not_created_exception.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/utils/currency_pairs_utils.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:http/http.dart' as http;
class StealthExExchangeProvider extends ExchangeProvider {
StealthExExchangeProvider() : super(pairList: supportedPairs(_notSupported));
static const List<CryptoCurrency> _notSupported = [];
static final apiKey = secrets.stealthExBearerToken;
static final _additionalFeePercent = double.tryParse(secrets.stealthExAdditionalFeePercent);
static const _baseUrl = 'https://api.stealthex.io';
static const _rangePath = '/v4/rates/range';
static const _amountPath = '/v4/rates/estimated-amount';
static const _exchangesPath = '/v4/exchanges';
@override
String get title => 'StealthEX';
@override
bool get isAvailable => true;
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
ExchangeProviderDescription get description => ExchangeProviderDescription.stealthEx;
@override
Future<bool> checkIsAvailable() async => true;
@override
Future<Limits> fetchLimits(
{required CryptoCurrency from,
required CryptoCurrency to,
required bool isFixedRateMode}) async {
final curFrom = isFixedRateMode ? to : from;
final curTo = isFixedRateMode ? from : to;
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final body = {
'route': {
'from': {'symbol': _getName(curFrom), 'network': _getNetwork(curFrom)},
'to': {'symbol': _getName(curTo), 'network': _getNetwork(curTo)}
},
'estimation': isFixedRateMode ? 'reversed' : 'direct',
'rate': isFixedRateMode ? 'fixed' : 'floating',
'additional_fee_percent': _additionalFeePercent,
};
try {
final response = await http.post(Uri.parse(_baseUrl + _rangePath),
headers: headers, body: json.encode(body));
if (response.statusCode != 200) {
throw Exception('StealthEx fetch limits failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final min = toDouble(responseJSON['min_amount']);
final max = responseJSON['max_amount'] as double?;
return Limits(min: min, max: max);
} catch (e) {
log(e.toString());
throw Exception('StealthEx failed to fetch limits');
}
}
@override
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode,
required bool isReceiveAmount}) async {
final response = await getEstimatedExchangeAmount(
from: from, to: to, amount: amount, isFixedRateMode: isFixedRateMode);
final estimatedAmount = response['estimated_amount'] as double? ?? 0.0;
return estimatedAmount > 0.0
? isFixedRateMode
? amount / estimatedAmount
: estimatedAmount / amount
: 0.0;
}
@override
Future<Trade> createTrade(
{required TradeRequest request,
required bool isFixedRateMode,
required bool isSendAll}) async {
String? rateId;
String? validUntil;
try {
if (isFixedRateMode) {
final response = await getEstimatedExchangeAmount(
from: request.fromCurrency,
to: request.toCurrency,
amount: double.parse(request.toAmount),
isFixedRateMode: isFixedRateMode);
rateId = response['rate_id'] as String?;
validUntil = response['valid_until'] as String?;
if (rateId == null) throw TradeNotCreatedException(description);
}
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final body = {
'route': {
'from': {
'symbol': _getName(request.fromCurrency),
'network': _getNetwork(request.fromCurrency)
},
'to': {'symbol': _getName(request.toCurrency), 'network': _getNetwork(request.toCurrency)}
},
'estimation': isFixedRateMode ? 'reversed' : 'direct',
'rate': isFixedRateMode ? 'fixed' : 'floating',
if (isFixedRateMode) 'rate_id': rateId,
'amount':
isFixedRateMode ? double.parse(request.toAmount) : double.parse(request.fromAmount),
'address': request.toAddress,
'refund_address': request.refundAddress,
'additional_fee_percent': _additionalFeePercent,
};
final response = await http.post(Uri.parse(_baseUrl + _exchangesPath),
headers: headers, body: json.encode(body));
if (response.statusCode != 201) {
throw Exception('StealthEx create trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final deposit = responseJSON['deposit'] as Map<String, dynamic>;
final withdrawal = responseJSON['withdrawal'] as Map<String, dynamic>;
final id = responseJSON['id'] as String;
final from = deposit['symbol'] as String;
final to = withdrawal['symbol'] as String;
final payoutAddress = withdrawal['address'] as String;
final depositAddress = deposit['address'] as String;
final refundAddress = responseJSON['refund_address'] as String;
final depositAmount = toDouble(deposit['amount']);
final receiveAmount = toDouble(withdrawal['amount']);
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final createdAt = DateTime.parse(createdAtString);
final expiredAt = validUntil != null
? DateTime.parse(validUntil)
: DateTime.now().add(Duration(minutes: 5));
CryptoCurrency fromCurrency;
if (request.fromCurrency.tag != null && request.fromCurrency.title.toLowerCase() == from) {
fromCurrency = request.fromCurrency;
} else {
fromCurrency = CryptoCurrency.fromString(from);
}
CryptoCurrency toCurrency;
if (request.toCurrency.tag != null && request.toCurrency.title.toLowerCase() == to) {
toCurrency = request.toCurrency;
} else {
toCurrency = CryptoCurrency.fromString(to);
}
return Trade(
id: id,
from: fromCurrency,
to: toCurrency,
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount.toString(),
receiveAmount: receiveAmount.toString(),
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
expiredAt: expiredAt,
);
} catch (e) {
log(e.toString());
throw TradeNotCreatedException(description);
}
}
@override
Future<Trade> findTradeById({required String id}) async {
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final uri = Uri.parse('$_baseUrl$_exchangesPath/$id');
final response = await http.get(uri, headers: headers);
if (response.statusCode != 200) {
throw Exception('StealthEx fetch trade failed: ${response.body}');
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final deposit = responseJSON['deposit'] as Map<String, dynamic>;
final withdrawal = responseJSON['withdrawal'] as Map<String, dynamic>;
final respId = responseJSON['id'] as String;
final from = deposit['symbol'] as String;
final to = withdrawal['symbol'] as String;
final payoutAddress = withdrawal['address'] as String;
final depositAddress = deposit['address'] as String;
final refundAddress = responseJSON['refund_address'] as String;
final depositAmount = toDouble(deposit['amount']);
final receiveAmount = toDouble(withdrawal['amount']);
final status = responseJSON['status'] as String;
final createdAtString = responseJSON['created_at'] as String;
final createdAt = DateTime.parse(createdAtString);
return Trade(
id: respId,
from: CryptoCurrency.fromString(from),
to: CryptoCurrency.fromString(to),
provider: description,
inputAddress: depositAddress,
payoutAddress: payoutAddress,
refundAddress: refundAddress,
amount: depositAmount.toString(),
receiveAmount: receiveAmount.toString(),
state: TradeState.deserialize(raw: status),
createdAt: createdAt,
isRefund: status == 'refunded',
);
}
Future<Map<String, dynamic>> getEstimatedExchangeAmount(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
required bool isFixedRateMode}) async {
final headers = {'Authorization': apiKey, 'Content-Type': 'application/json'};
final body = {
'route': {
'from': {'symbol': _getName(from), 'network': _getNetwork(from)},
'to': {'symbol': _getName(to), 'network': _getNetwork(to)}
},
'estimation': isFixedRateMode ? 'reversed' : 'direct',
'rate': isFixedRateMode ? 'fixed' : 'floating',
'amount': amount,
'additional_fee_percent': _additionalFeePercent,
};
try {
final response = await http.post(Uri.parse(_baseUrl + _amountPath),
headers: headers, body: json.encode(body));
if (response.statusCode != 200) return {};
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final rate = responseJSON['rate'] as Map<String, dynamic>?;
return {
'estimated_amount': responseJSON['estimated_amount'] as double?,
if (rate != null) 'valid_until': rate['valid_until'] as String?,
if (rate != null) 'rate_id': rate['id'] as String?
};
} catch (e) {
log(e.toString());
return {};
}
}
double toDouble(dynamic value) {
if (value is int) {
return value.toDouble();
} else if (value is double) {
return value;
} else {
return 0.0;
}
}
String _getName(CryptoCurrency currency) {
if (currency == CryptoCurrency.usdcEPoly) return 'usdce';
return currency.title.toLowerCase();
}
String _getNetwork(CryptoCurrency currency) {
if (currency.tag == null) return 'mainnet';
if (currency == CryptoCurrency.maticpoly) return 'mainnet';
if (currency.tag == 'POLY') return 'matic';
return currency.tag!.toLowerCase();
}
}

View file

@ -40,7 +40,6 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
static const exchanging = TradeState(raw: 'exchanging', title: 'Exchanging'); static const exchanging = TradeState(raw: 'exchanging', title: 'Exchanging');
static const sending = TradeState(raw: 'sending', title: 'Sending'); static const sending = TradeState(raw: 'sending', title: 'Sending');
static const success = TradeState(raw: 'success', title: 'Success'); static const success = TradeState(raw: 'success', title: 'Success');
static TradeState deserialize({required String raw}) { static TradeState deserialize({required String raw}) {
switch (raw) { switch (raw) {
@ -107,6 +106,7 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
case 'waitingAuthorization': case 'waitingAuthorization':
return waitingAuthorization; return waitingAuthorization;
case 'failed': case 'failed':
case 'error':
return failed; return failed;
case 'completed': case 'completed':
return completed; return completed;
@ -119,12 +119,14 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
case 'refunded': case 'refunded':
return refunded; return refunded;
case 'confirmation': case 'confirmation':
case 'verifying':
return confirmation; return confirmation;
case 'confirmed': case 'confirmed':
return confirmed; return confirmed;
case 'exchanging': case 'exchanging':
return exchanging; return exchanging;
case 'sending': case 'sending':
case 'sending_confirmation':
return sending; return sending;
case 'success': case 'success':
case 'done': case 'done':

View file

@ -48,10 +48,14 @@ final rootKey = GlobalKey<RootState>();
final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>(); final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>();
Future<void> main({Key? topLevelKey}) async { Future<void> main({Key? topLevelKey}) async {
await runAppWithZone(topLevelKey: topLevelKey);
}
Future<void> runAppWithZone({Key? topLevelKey}) async {
bool isAppRunning = false; bool isAppRunning = false;
await runZonedGuarded(() async { await runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = ExceptionHandler.onError; FlutterError.onError = ExceptionHandler.onError;
/// A callback that is invoked when an unhandled error occurs in the root /// A callback that is invoked when an unhandled error occurs in the root
@ -61,42 +65,14 @@ Future<void> main({Key? topLevelKey}) async {
return true; return true;
}; };
await initializeAppAtRoot();
await setDefaultMinimumWindowSize(); runApp(App());
await CakeHive.close();
await initializeAppConfigs();
runApp(App(key: topLevelKey));
isAppRunning = true; isAppRunning = true;
}, (error, stackTrace) async { }, (error, stackTrace) async {
if (!isAppRunning) { if (!isAppRunning) {
runApp( runApp(
MaterialApp( TopLevelErrorWidget(error: error, stackTrace: stackTrace),
debugShowCheckedModeBanner: false,
scrollBehavior: AppScrollBehavior(),
home: Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
child: Column(
children: [
Text(
'Error:\n${error.toString()}',
style: TextStyle(fontSize: 22),
),
Text(
'Stack trace:\n${stackTrace.toString()}',
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
),
); );
} }
@ -104,6 +80,12 @@ Future<void> main({Key? topLevelKey}) async {
}); });
} }
Future<void> initializeAppAtRoot({bool reInitializing = false}) async {
if (!reInitializing) await setDefaultMinimumWindowSize();
await CakeHive.close();
await initializeAppConfigs();
}
Future<void> initializeAppConfigs() async { Future<void> initializeAppConfigs() async {
setRootDirFromEnv(); setRootDirFromEnv();
final appDir = await getAppDir(); final appDir = await getAppDir();
@ -340,3 +322,41 @@ class _HomeState extends State<_Home> {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
} }
class TopLevelErrorWidget extends StatelessWidget {
const TopLevelErrorWidget({
required this.error,
required this.stackTrace,
super.key,
});
final Object error;
final StackTrace stackTrace;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
scrollBehavior: AppScrollBehavior(),
home: Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
child: Column(
children: [
Text(
'Error:\n${error.toString()}',
style: TextStyle(fontSize: 22),
),
Text(
'Stack trace:\n${stackTrace.toString()}',
style: TextStyle(fontSize: 16),
),
],
),
),
),
),
);
}
}

View file

@ -24,7 +24,9 @@ void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore
return; return;
} }
if (wallet.syncStatus is LostConnectionSyncStatus || wallet.syncStatus is FailedSyncStatus) { if (wallet.type != WalletType.bitcoin &&
(wallet.syncStatus is LostConnectionSyncStatus ||
wallet.syncStatus is FailedSyncStatus)) {
final alive = await settingsStore.getCurrentNode(wallet.type).requestNode(); final alive = await settingsStore.getCurrentNode(wallet.type).requestNode();
if (alive) { if (alive) {

View file

@ -17,6 +17,8 @@ import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/buy/webview_page.dart'; import 'package:cake_wallet/src/screens/buy/webview_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart'; import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart'; import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
@ -27,8 +29,8 @@ import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart';
import 'package:cake_wallet/src/screens/dashboard/sign_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/dashboard/sign_page.dart';
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart'; import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
@ -43,10 +45,9 @@ import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_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/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart'; import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
@ -69,11 +70,9 @@ import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart';
import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart'; import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart'; import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart'; import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
@ -85,19 +84,18 @@ import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart'; import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart'; import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart'; import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_page.dart'; import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart'; import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
@ -106,7 +104,7 @@ import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart'; import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wallet_type_utils.dart';
@ -120,7 +118,7 @@ import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'src/screens/dashboard/pages/nft_import_page.dart'; import 'src/screens/dashboard/pages/nft_import_page.dart';
late RouteSettings currentRouteSettings; late RouteSettings currentRouteSettings;
@ -135,7 +133,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.newWalletFromWelcome: case Routes.newWalletFromWelcome:
if (SettingsStoreBase.walletPasswordDirectInput) { if (SettingsStoreBase.walletPasswordDirectInput) {
if (availableWalletTypes.length == 1) { if (availableWalletTypes.length == 1) {
return createRoute(RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first)); return createRoute(
RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first));
} else { } else {
return createRoute(RouteSettings(name: Routes.newWalletType)); return createRoute(RouteSettings(name: Routes.newWalletType));
} }
@ -162,10 +161,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.newWallet: case Routes.newWallet:
final type = settings.arguments as WalletType; final type = settings.arguments as WalletType;
final walletNewVM = getIt.get<WalletNewVM>(param1: type); final walletNewVM = getIt.get<WalletNewVM>(param1: type);
final seedTypeViewModel = getIt.get<SeedTypeViewModel>(); final seedSettingsViewModel = getIt.get<SeedSettingsViewModel>();
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => NewWalletPage(walletNewVM, seedTypeViewModel)); builder: (_) => NewWalletPage(walletNewVM, seedSettingsViewModel));
case Routes.chooseHardwareWalletAccount: case Routes.chooseHardwareWalletAccount:
final arguments = settings.arguments as List<dynamic>; final arguments = settings.arguments as List<dynamic>;
@ -348,16 +347,14 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.auth: case Routes.auth:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) builder: (_) => SettingsStoreBase.walletPasswordDirectInput
=> SettingsStoreBase.walletPasswordDirectInput ? getIt.get<WalletUnlockPage>(
? getIt.get<WalletUnlockPage>( param1: WalletUnlockArguments(
param1: WalletUnlockArguments(
callback: settings.arguments as OnAuthenticationFinished), callback: settings.arguments as OnAuthenticationFinished),
instanceName: 'wallet_unlock_verifiable', instanceName: 'wallet_unlock_verifiable',
param2: true) param2: true)
: getIt.get<AuthPage>( : getIt.get<AuthPage>(
param1: settings.arguments as OnAuthenticationFinished, param1: settings.arguments as OnAuthenticationFinished, param2: true));
param2: true));
case Routes.totpAuthCodePage: case Routes.totpAuthCodePage:
final args = settings.arguments as TotpAuthArgumentsModel; final args = settings.arguments as TotpAuthArgumentsModel;
@ -371,28 +368,25 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.walletUnlockLoadable: case Routes.walletUnlockLoadable:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) builder: (_) => getIt.get<WalletUnlockPage>(
=> getIt.get<WalletUnlockPage>(
param1: settings.arguments as WalletUnlockArguments, param1: settings.arguments as WalletUnlockArguments,
instanceName: 'wallet_unlock_loadable', instanceName: 'wallet_unlock_loadable',
param2: true)); param2: true));
case Routes.unlock: case Routes.unlock:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) builder: (_) => SettingsStoreBase.walletPasswordDirectInput
=> SettingsStoreBase.walletPasswordDirectInput ? WillPopScope(
? WillPopScope( child: getIt.get<WalletUnlockPage>(
child: getIt.get<WalletUnlockPage>(
param1: WalletUnlockArguments( param1: WalletUnlockArguments(
callback: settings.arguments as OnAuthenticationFinished), callback: settings.arguments as OnAuthenticationFinished),
param2: false, param2: false,
instanceName: 'wallet_unlock_verifiable'), instanceName: 'wallet_unlock_verifiable'),
onWillPop: () async => false) onWillPop: () async => false)
: WillPopScope( : WillPopScope(
child: getIt.get<AuthPage>( child: getIt.get<AuthPage>(
param1: settings.arguments as OnAuthenticationFinished, param1: settings.arguments as OnAuthenticationFinished, param2: false),
param2: false),
onWillPop: () async => false)); onWillPop: () async => false));
case Routes.silentPaymentsSettings: case Routes.silentPaymentsSettings:
@ -437,11 +431,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (context) => WillPopScope( builder: (context) => WillPopScope(
child: SettingsStoreBase.walletPasswordDirectInput child: SettingsStoreBase.walletPasswordDirectInput
? getIt.get<WalletUnlockPage>(instanceName: 'wallet_password_login') ? getIt.get<WalletUnlockPage>(instanceName: 'wallet_password_login')
: getIt.get<AuthPage>(instanceName: 'login'), : getIt.get<AuthPage>(instanceName: 'login'),
onWillPop: () async => onWillPop: () async =>
// FIX-ME: Additional check does it works correctly // FIX-ME: Additional check does it works correctly
(await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ?? false)), (await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ??
false)),
fullscreenDialog: true); fullscreenDialog: true);
case Routes.newPowNode: case Routes.newPowNode:
@ -537,8 +532,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.support: case Routes.support:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true, builder: (_) => getIt.get<SupportPage>());
builder: (_) => getIt.get<SupportPage>());
case Routes.supportLiveChat: case Routes.supportLiveChat:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportChatPage>()); return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportChatPage>());
@ -567,8 +561,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.cakePayBuyCardPage: case Routes.cakePayBuyCardPage:
final args = settings.arguments as List; final args = settings.arguments as List;
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayBuyCardPage>(param1: args));
builder: (_) => getIt.get<CakePayBuyCardPage>(param1: args));
case Routes.cakePayBuyCardDetailPage: case Routes.cakePayBuyCardDetailPage:
final args = settings.arguments as List; final args = settings.arguments as List;
@ -582,7 +575,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.cakePayVerifyOtpPage: case Routes.cakePayVerifyOtpPage:
final args = settings.arguments as List; final args = settings.arguments as List;
return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayVerifyOtpPage>(param1: args)); return CupertinoPageRoute<void>(
builder: (_) => getIt.get<CakePayVerifyOtpPage>(param1: args));
case Routes.cakePayAccountPage: case Routes.cakePayAccountPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayAccountPage>()); return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayAccountPage>());
@ -597,16 +591,19 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.advancedPrivacySettings: case Routes.advancedPrivacySettings:
final args = settings.arguments as Map<String, dynamic>; final args = settings.arguments as Map<String, dynamic>;
final type = args['type'] as WalletType; final type = args['type'] as WalletType;
final isFromRestore = args['isFromRestore'] as bool? ?? false;
final useTestnet = args['useTestnet'] as bool; final useTestnet = args['useTestnet'] as bool;
final toggleTestnet = args['toggleTestnet'] as Function(bool? val); final toggleTestnet = args['toggleTestnet'] as Function(bool? val);
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => AdvancedPrivacySettingsPage( builder: (_) => AdvancedPrivacySettingsPage(
useTestnet, isFromRestore: isFromRestore,
toggleTestnet, useTestnet: useTestnet,
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type), toggleUseTestnet: toggleTestnet,
getIt.get<NodeCreateOrEditViewModel>(param1: type, param2: false), advancedPrivacySettingsViewModel:
getIt.get<SeedTypeViewModel>(), getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
nodeViewModel: getIt.get<NodeCreateOrEditViewModel>(param1: type, param2: false),
seedSettingsViewModel: getIt.get<SeedSettingsViewModel>(),
)); ));
case Routes.anonPayInvoicePage: case Routes.anonPayInvoicePage:

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
@ -36,7 +37,8 @@ class TradeRow extends StatelessWidget {
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(50), borderRadius: BorderRadius.circular(50),
child: Image.asset(provider.image, width: 36, height: 36)), child: ImageUtil.getImageFromPath(
imagePath: provider.image, height: 36, width: 36)),
SizedBox(width: 12), SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(

View file

@ -1,23 +1,7 @@
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/entities/generate_name.dart';
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart'; import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/themes/extensions/address_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:polyseed/polyseed.dart';
class VerifyForm extends StatefulWidget { class VerifyForm extends StatefulWidget {
VerifyForm({ VerifyForm({

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart'; import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart'; import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_bar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -102,7 +103,8 @@ class ExchangeConfirmPage extends BasePage {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
(trade.provider.image?.isNotEmpty ?? false) (trade.provider.image?.isNotEmpty ?? false)
? Image.asset(trade.provider.image, height: 50) ? ImageUtil.getImageFromPath(
imagePath: trade.provider.image, width: 50)
: const SizedBox(), : const SizedBox(),
if (!trade.provider.horizontalLogo) if (!trade.provider.horizontalLogo)
Padding( Padding(

View file

@ -40,13 +40,11 @@ class NanoChangeRepPage extends BasePage {
(node) => node.account == currentRepAccount, (node) => node.account == currentRepAccount,
orElse: () => N2Node( orElse: () => N2Node(
account: currentRepAccount, account: currentRepAccount,
alias: currentRepAccount,
score: 0, score: 0,
uptime: "???", uptime: "???",
weight: 0, weight: 0,
), ),
); );
return currentNode; return currentNode;
} }
@ -57,9 +55,7 @@ class NanoChangeRepPage extends BasePage {
child: FutureBuilder( child: FutureBuilder(
future: nano!.getN2Reps(_wallet), future: nano!.getN2Reps(_wallet),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.data == null) { final reps = snapshot.data ?? [];
return SizedBox();
}
return Container( return Container(
padding: EdgeInsets.only(left: 24, right: 24), padding: EdgeInsets.only(left: 24, right: 24),
@ -101,29 +97,35 @@ class NanoChangeRepPage extends BasePage {
), ),
_buildSingleRepresentative( _buildSingleRepresentative(
context, context,
getCurrentRepNode(snapshot.data as List<N2Node>), getCurrentRepNode(reps),
isList: false, isList: false,
divider: false,
), ),
Divider(height: 20), if (reps.isNotEmpty) ...[
Container( Divider(height: 20),
margin: EdgeInsets.only(top: 12), Container(
child: Text( margin: EdgeInsets.only(top: 12),
S.current.nano_pick_new_rep, child: Text(
style: TextStyle( S.current.nano_pick_new_rep,
fontSize: 16, style: TextStyle(
fontWeight: FontWeight.w700, fontSize: 16,
fontWeight: FontWeight.w700,
),
), ),
), ),
), Divider(height: 20),
],
], ],
), ),
], ],
), ),
contentPadding: EdgeInsets.only(bottom: 24), contentPadding: EdgeInsets.only(bottom: 24),
content: Container( content: Container(
child: Column( child: reps.isNotEmpty
children: _getRepresentativeWidgets(context, snapshot.data as List<N2Node>), ? Column(
), children: _getRepresentativeWidgets(context, reps),
)
: SizedBox(),
), ),
bottomSectionPadding: EdgeInsets.only(bottom: 24), bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer( bottomSection: Observer(
@ -207,19 +209,22 @@ class NanoChangeRepPage extends BasePage {
final List<Widget> ret = []; final List<Widget> ret = [];
for (final N2Node node in list) { for (final N2Node node in list) {
if (node.alias != null && node.alias!.trim().isNotEmpty) { if (node.alias != null && node.alias!.trim().isNotEmpty) {
ret.add(_buildSingleRepresentative(context, node)); bool divider = node != list.first;
ret.add(_buildSingleRepresentative(context, node, divider: divider, isList: true));
} }
} }
return ret; return ret;
} }
Widget _buildSingleRepresentative(BuildContext context, N2Node rep, {bool isList = true}) { Widget _buildSingleRepresentative(
BuildContext context,
N2Node rep, {
bool isList = true,
bool divider = false,
}) {
return Column( return Column(
children: <Widget>[ children: <Widget>[
if (isList) if (divider) Divider(height: 2),
Divider(
height: 2,
),
TextButton( TextButton(
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -244,11 +249,11 @@ class NanoChangeRepPage extends BasePage {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
_sanitizeAlias(rep.alias), rep.alias ?? rep.account!,
style: TextStyle( style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor, color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 18, fontSize: rep.alias == null ? 14 : 18,
), ),
), ),
Container( Container(
@ -337,11 +342,4 @@ class NanoChangeRepPage extends BasePage {
], ],
); );
} }
String _sanitizeAlias(String? alias) {
if (alias != null) {
return alias.replaceAll(RegExp(r'[^a-zA-Z_.!?_;:-]'), '');
}
return '';
}
} }

View file

@ -3,52 +3,61 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/seed_phrase_length.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart';
import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class AdvancedPrivacySettingsPage extends BasePage { class AdvancedPrivacySettingsPage extends BasePage {
AdvancedPrivacySettingsPage(this.useTestnet, this.toggleUseTestnet, AdvancedPrivacySettingsPage({
this.advancedPrivacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel); required this.isFromRestore,
required this.useTestnet,
required this.toggleUseTestnet,
required this.advancedPrivacySettingsViewModel,
required this.nodeViewModel,
required this.seedSettingsViewModel,
});
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel; final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
final NodeCreateOrEditViewModel nodeViewModel; final NodeCreateOrEditViewModel nodeViewModel;
final SeedTypeViewModel seedTypeViewModel; final SeedSettingsViewModel seedSettingsViewModel;
@override @override
String get title => S.current.privacy_settings; String get title => S.current.privacy_settings;
final bool isFromRestore;
final bool useTestnet; final bool useTestnet;
final Function(bool? val) toggleUseTestnet; final Function(bool? val) toggleUseTestnet;
@override @override
Widget body(BuildContext context) => AdvancedPrivacySettingsBody(useTestnet, toggleUseTestnet, Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(isFromRestore, useTestnet,
advancedPrivacySettingsViewModel, nodeViewModel, seedTypeViewModel); toggleUseTestnet, advancedPrivacySettingsViewModel, nodeViewModel, seedSettingsViewModel);
} }
class AdvancedPrivacySettingsBody extends StatefulWidget { class _AdvancedPrivacySettingsBody extends StatefulWidget {
const AdvancedPrivacySettingsBody(this.useTestnet, this.toggleUseTestnet, const _AdvancedPrivacySettingsBody(this.isFromRestore, this.useTestnet, this.toggleUseTestnet,
this.privacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel, this.privacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel,
{Key? key}) {Key? key})
: super(key: key); : super(key: key);
final AdvancedPrivacySettingsViewModel privacySettingsViewModel; final AdvancedPrivacySettingsViewModel privacySettingsViewModel;
final NodeCreateOrEditViewModel nodeViewModel; final NodeCreateOrEditViewModel nodeViewModel;
final SeedTypeViewModel seedTypeViewModel; final SeedSettingsViewModel seedTypeViewModel;
final bool isFromRestore;
final bool useTestnet; final bool useTestnet;
final Function(bool? val) toggleUseTestnet; final Function(bool? val) toggleUseTestnet;
@ -56,15 +65,25 @@ class AdvancedPrivacySettingsBody extends StatefulWidget {
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState(); _AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
} }
class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBody> { class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBody> {
_AdvancedPrivacySettingsBodyState(); final TextEditingController passphraseController = TextEditingController();
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
bool? testnetValue; bool? testnetValue;
bool obscurePassphrase = true;
@override
void initState() {
passphraseController.text = widget.seedTypeViewModel.passphrase ?? '';
passphraseController
.addListener(() => widget.seedTypeViewModel.setPassphrase(passphraseController.text));
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (testnetValue == null && widget.useTestnet != null) { if (testnetValue == null && widget.useTestnet) {
testnetValue = widget.useTestnet; testnetValue = widget.useTestnet;
} }
@ -97,6 +116,61 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
), ),
); );
}), }),
if (widget.privacySettingsViewModel.hasSeedTypeOption)
Observer(builder: (_) {
return SettingsChoicesCell(
ChoicesListItem<MoneroSeedType>(
title: S.current.seedtype,
items: MoneroSeedType.all,
selectedItem: widget.seedTypeViewModel.moneroSeedType,
onItemSelected: widget.seedTypeViewModel.setMoneroSeedType,
),
);
}),
if ([WalletType.bitcoin, WalletType.litecoin]
.contains(widget.privacySettingsViewModel.type))
Observer(builder: (_) {
return SettingsChoicesCell(
ChoicesListItem<BitcoinSeedType>(
title: S.current.seedtype,
items: BitcoinSeedType.all,
selectedItem: widget.seedTypeViewModel.bitcoinSeedType,
onItemSelected: widget.seedTypeViewModel.setBitcoinSeedType,
),
);
}),
if (!widget.isFromRestore) ...[
Observer(builder: (_) {
if (widget.privacySettingsViewModel.hasSeedPhraseLengthOption)
return SettingsPickerCell<SeedPhraseLength>(
title: S.current.seed_phrase_length,
items: SeedPhraseLength.values,
selectedItem: widget.privacySettingsViewModel.seedPhraseLength,
onItemSelected: (SeedPhraseLength length) {
widget.privacySettingsViewModel.setSeedPhraseLength(length);
},
);
return Container();
}),
if (widget.privacySettingsViewModel.hasPassphraseOption)
Padding(
padding: EdgeInsets.all(24),
child: BaseTextFormField(
hintText: S.current.passphrase,
controller: passphraseController,
obscureText: obscurePassphrase,
suffixIcon: GestureDetector(
onTap: () => setState(() {
obscurePassphrase = !obscurePassphrase;
}),
child: Icon(
Icons.remove_red_eye,
color: obscurePassphrase ? Colors.black54 : Colors.black26,
),
),
),
),
],
Observer(builder: (_) { Observer(builder: (_) {
return Column( return Column(
children: [ children: [
@ -122,31 +196,9 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
], ],
); );
}), }),
if (widget.privacySettingsViewModel.hasSeedPhraseLengthOption)
Observer(builder: (_) {
return SettingsPickerCell<SeedPhraseLength>(
title: S.current.seed_phrase_length,
items: SeedPhraseLength.values,
selectedItem: widget.privacySettingsViewModel.seedPhraseLength,
onItemSelected: (SeedPhraseLength length) {
widget.privacySettingsViewModel.setSeedPhraseLength(length);
},
);
}),
if (widget.privacySettingsViewModel.hasSeedTypeOption)
Observer(builder: (_) {
return SettingsChoicesCell(
ChoicesListItem<SeedType>(
title: S.current.seedtype,
items: SeedType.all,
selectedItem: widget.seedTypeViewModel.moneroSeedType,
onItemSelected: widget.seedTypeViewModel.setMoneroSeedType,
),
);
}),
if (widget.privacySettingsViewModel.type == WalletType.bitcoin) if (widget.privacySettingsViewModel.type == WalletType.bitcoin)
Builder(builder: (_) { Builder(builder: (_) {
final val = testnetValue!; final val = testnetValue ?? false;
return SettingsSwitcherCell( return SettingsSwitcherCell(
title: S.current.use_testnet, title: S.current.use_testnet,
value: val, value: val,
@ -154,7 +206,7 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
setState(() { setState(() {
testnetValue = !val; testnetValue = !val;
}); });
widget.toggleUseTestnet!.call(testnetValue); widget.toggleUseTestnet.call(testnetValue);
}); });
}), }),
], ],
@ -203,4 +255,11 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
), ),
); );
} }
@override
void dispose() {
passphraseController
.removeListener(() => widget.seedTypeViewModel.setPassphrase(passphraseController.text));
super.dispose();
}
} }

View file

@ -1,36 +1,35 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/entities/generate_name.dart'; import 'package:cake_wallet/entities/generate_name.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/seed_language_picker.dart'; import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class NewWalletPage extends BasePage { class NewWalletPage extends BasePage {
NewWalletPage(this._walletNewVM, this._seedTypeViewModel); NewWalletPage(this._walletNewVM, this._seedSettingsViewModel);
final WalletNewVM _walletNewVM; final WalletNewVM _walletNewVM;
final SeedTypeViewModel _seedTypeViewModel; final SeedSettingsViewModel _seedSettingsViewModel;
final walletNameImage = Image.asset('assets/images/wallet_name.png'); final walletNameImage = Image.asset('assets/images/wallet_name.png');
@ -51,15 +50,15 @@ class NewWalletPage extends BasePage {
Widget body(BuildContext context) => WalletNameForm( Widget body(BuildContext context) => WalletNameForm(
_walletNewVM, _walletNewVM,
currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage,
_seedTypeViewModel); _seedSettingsViewModel);
} }
class WalletNameForm extends StatefulWidget { class WalletNameForm extends StatefulWidget {
WalletNameForm(this._walletNewVM, this.walletImage, this._seedTypeViewModel); WalletNameForm(this._walletNewVM, this.walletImage, this._seedSettingsViewModel);
final WalletNewVM _walletNewVM; final WalletNewVM _walletNewVM;
final Image walletImage; final Image walletImage;
final SeedTypeViewModel _seedTypeViewModel; final SeedSettingsViewModel _seedSettingsViewModel;
@override @override
_WalletNameFormState createState() => _WalletNameFormState(_walletNewVM); _WalletNameFormState createState() => _WalletNameFormState(_walletNewVM);
@ -113,7 +112,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
} }
}); });
_setSeedType(SeedType.defaultSeedType); _setSeedType(MoneroSeedType.defaultSeedType);
super.initState(); super.initState();
} }
@ -291,12 +290,12 @@ class _WalletNameFormState extends State<WalletNameForm> {
padding: EdgeInsets.only(top: 24), padding: EdgeInsets.only(top: 24),
child: SelectButton( child: SelectButton(
key: ValueKey('new_wallet_page_monero_seed_type_button_key'), key: ValueKey('new_wallet_page_monero_seed_type_button_key'),
text: widget._seedTypeViewModel.moneroSeedType.title, text: widget._seedSettingsViewModel.moneroSeedType.title,
onTap: () async { onTap: () async {
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (_) => Picker( builder: (_) => Picker(
items: SeedType.all, items: MoneroSeedType.all,
selectedAtIndex: isPolyseed ? 1 : 0, selectedAtIndex: isPolyseed ? 1 : 0,
onItemSelected: _setSeedType, onItemSelected: _setSeedType,
isSeparated: false, isSeparated: false,
@ -315,8 +314,8 @@ class _WalletNameFormState extends State<WalletNameForm> {
buttonKey: ValueKey('new_wallet_page_seed_language_selector_button_key'), buttonKey: ValueKey('new_wallet_page_seed_language_selector_button_key'),
initialSelected: defaultSeedLanguage, initialSelected: defaultSeedLanguage,
seedType: _walletNewVM.hasSeedType seedType: _walletNewVM.hasSeedType
? widget._seedTypeViewModel.moneroSeedType ? widget._seedSettingsViewModel.moneroSeedType
: SeedType.legacy, : MoneroSeedType.legacy,
), ),
), ),
) )
@ -389,10 +388,10 @@ class _WalletNameFormState extends State<WalletNameForm> {
_formProcessing = false; _formProcessing = false;
} }
bool get isPolyseed => widget._seedTypeViewModel.moneroSeedType == SeedType.polyseed; bool get isPolyseed => widget._seedSettingsViewModel.moneroSeedType == MoneroSeedType.polyseed;
void _setSeedType(SeedType item) { void _setSeedType(MoneroSeedType item) {
widget._seedTypeViewModel.setMoneroSeedType(item); widget._seedSettingsViewModel.setMoneroSeedType(item);
_languageSelectorKey.currentState?.selected = defaultSeedLanguage; // Reset Seed language _languageSelectorKey.currentState?.selected = defaultSeedLanguage; // Reset Seed language
} }
} }

View file

@ -9,35 +9,34 @@ import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart'; import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:polyseed/polyseed.dart'; import 'package:polyseed/polyseed.dart';
class WalletRestoreFromSeedForm extends StatefulWidget { class WalletRestoreFromSeedForm extends StatefulWidget {
WalletRestoreFromSeedForm( WalletRestoreFromSeedForm({Key? key,
{Key? key, required this.displayLanguageSelector,
required this.displayLanguageSelector, required this.displayBlockHeightSelector,
required this.displayBlockHeightSelector, required this.displayPassphrase,
required this.displayPassphrase, required this.type,
required this.type, required this.displayWalletPassword,
required this.displayWalletPassword, required this.seedSettingsViewModel,
required this.seedTypeViewModel, this.blockHeightFocusNode,
this.blockHeightFocusNode, this.onHeightOrDateEntered,
this.onHeightOrDateEntered, this.onSeedChange,
this.onSeedChange, this.onLanguageChange,
this.onLanguageChange, this.onPasswordChange,
this.onPasswordChange, this.onRepeatedPasswordChange,
this.onRepeatedPasswordChange}) }) : super(key: key);
: super(key: key);
final WalletType type; final WalletType type;
final bool displayLanguageSelector; final bool displayLanguageSelector;
final bool displayBlockHeightSelector; final bool displayBlockHeightSelector;
final bool displayWalletPassword; final bool displayWalletPassword;
final bool displayPassphrase; final bool displayPassphrase;
final SeedTypeViewModel seedTypeViewModel; final SeedSettingsViewModel seedSettingsViewModel;
final FocusNode? blockHeightFocusNode; final FocusNode? blockHeightFocusNode;
final Function(bool)? onHeightOrDateEntered; final Function(bool)? onHeightOrDateEntered;
final void Function(String)? onSeedChange; final void Function(String)? onSeedChange;
@ -58,7 +57,9 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
languageController = TextEditingController(), languageController = TextEditingController(),
nameTextEditingController = TextEditingController(), nameTextEditingController = TextEditingController(),
passwordTextEditingController = displayWalletPassword ? TextEditingController() : null, passwordTextEditingController = displayWalletPassword ? TextEditingController() : null,
repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null, repeatedPasswordTextEditingController = displayWalletPassword
? TextEditingController()
: null,
passphraseController = TextEditingController(), passphraseController = TextEditingController(),
seedTypeController = TextEditingController(); seedTypeController = TextEditingController();
@ -75,10 +76,11 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
String language; String language;
void Function()? passwordListener; void Function()? passwordListener;
void Function()? repeatedPasswordListener; void Function()? repeatedPasswordListener;
void Function()? passphraseListener;
@override @override
void initState() { void initState() {
_setSeedType(widget.seedTypeViewModel.moneroSeedType); _setSeedType(widget.seedSettingsViewModel.moneroSeedType);
_setLanguageLabel(language); _setLanguageLabel(language);
if (passwordTextEditingController != null) { if (passwordTextEditingController != null) {
@ -87,14 +89,19 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
} }
if (repeatedPasswordTextEditingController != null) { if (repeatedPasswordTextEditingController != null) {
repeatedPasswordListener = () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text); repeatedPasswordListener =
() => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text);
repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!); repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!);
} }
passphraseListener = () => widget.seedSettingsViewModel.setPassphrase(passphraseController.text);
passphraseController.addListener(passphraseListener!);
moneroSeedTypeReaction = moneroSeedTypeReaction =
reaction((_) => widget.seedTypeViewModel.moneroSeedType, (SeedType item) { reaction((_) => widget.seedSettingsViewModel.moneroSeedType, (MoneroSeedType item) {
_setSeedType(item); _setSeedType(item);
_changeLanguage('English'); _changeLanguage('English');
}); });
super.initState(); super.initState();
} }
@ -110,6 +117,9 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
if (repeatedPasswordListener != null) { if (repeatedPasswordListener != null) {
repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!);
} }
passphraseController.removeListener(passphraseListener!);
super.dispose(); super.dispose();
} }
@ -118,11 +128,13 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
Polyseed.isValidSeed(seed)) { Polyseed.isValidSeed(seed)) {
final lang = PolyseedLang.getByPhrase(seed); final lang = PolyseedLang.getByPhrase(seed);
_changeSeedType(SeedType.polyseed); _changeSeedType(MoneroSeedType.polyseed);
_changeLanguage(lang.nameEnglish); _changeLanguage(lang.nameEnglish);
} }
if (widget.type == WalletType.wownero && seed.split(" ").length == 14) { if (widget.type == WalletType.wownero && seed
_changeSeedType(SeedType.wowneroSeed); .split(" ")
.length == 14) {
_changeSeedType(MoneroSeedType.wowneroSeed);
_changeLanguage("English"); _changeLanguage("English");
} }
widget.onSeedChange?.call(seed); widget.onSeedChange?.call(seed);
@ -141,7 +153,9 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
BaseTextFormField( BaseTextFormField(
key: ValueKey('wallet_restore_from_seed_wallet_name_textfield_key'), key: ValueKey('wallet_restore_from_seed_wallet_name_textfield_key'),
controller: nameTextEditingController, controller: nameTextEditingController,
hintText: S.of(context).wallet_name, hintText: S
.of(context)
.wallet_name,
suffixIcon: IconButton( suffixIcon: IconButton(
key: ValueKey('wallet_restore_from_seed_wallet_name_refresh_button_key'), key: ValueKey('wallet_restore_from_seed_wallet_name_refresh_button_key'),
onPressed: () async { onPressed: () async {
@ -158,7 +172,9 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0), borderRadius: BorderRadius.circular(6.0),
color: Theme.of(context).hintColor, color: Theme
.of(context)
.hintColor,
), ),
width: 34, width: 34,
height: 34, height: 34,
@ -188,13 +204,14 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
onTap: () async { onTap: () async {
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (_) => Picker( builder: (_) =>
Picker(
items: _getItems(), items: _getItems(),
selectedAtIndex: isPolyseed selectedAtIndex: isPolyseed
? 1 ? 1
: seedTypeController.value.text.contains("14") : seedTypeController.value.text.contains("14")
? 2 ? 2
: 0, : 0,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
onItemSelected: _changeSeedType, onItemSelected: _changeSeedType,
isSeparated: false, isSeparated: false,
@ -216,37 +233,43 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
if (widget.displayWalletPassword) if (widget.displayWalletPassword)
...[BaseTextFormField( ...[BaseTextFormField(
controller: passwordTextEditingController, controller: passwordTextEditingController,
hintText: S.of(context).password, hintText: S
.of(context)
.password,
obscureText: true), obscureText: true),
BaseTextFormField( BaseTextFormField(
controller: repeatedPasswordTextEditingController, controller: repeatedPasswordTextEditingController,
hintText: S.of(context).repeat_wallet_password, hintText: S
obscureText: true)], .of(context)
.repeat_wallet_password,
obscureText: true)
],
if (widget.displayLanguageSelector) if (widget.displayLanguageSelector)
if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector) if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector)
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (_) => SeedLanguagePicker( builder: (_) =>
selected: language, SeedLanguagePicker(
onItemSelected: _changeLanguage, selected: language,
seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy, onItemSelected: _changeLanguage,
)); seedType: isPolyseed ? MoneroSeedType.polyseed : MoneroSeedType.legacy,
}, ));
child: Container( },
color: Colors.transparent, child: Container(
padding: EdgeInsets.only(top: 20.0), color: Colors.transparent,
child: IgnorePointer( padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField( child: IgnorePointer(
controller: languageController, child: BaseTextFormField(
enableInteractiveSelection: false, controller: languageController,
readOnly: true, enableInteractiveSelection: false,
suffixIcon: expandIcon, readOnly: true,
suffixIcon: expandIcon,
),
), ),
), ),
), ),
),
if ((!isPolyseed) && widget.displayBlockHeightSelector) if ((!isPolyseed) && widget.displayBlockHeightSelector)
BlockchainHeightWidget( BlockchainHeightWidget(
focusNode: widget.blockHeightFocusNode, focusNode: widget.blockHeightFocusNode,
@ -267,17 +290,20 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
} }
bool get isPolyseed => bool get isPolyseed =>
widget.seedTypeViewModel.moneroSeedType == SeedType.polyseed && widget.seedSettingsViewModel.moneroSeedType == MoneroSeedType.polyseed &&
(widget.type == WalletType.monero || widget.type == WalletType.wownero); (widget.type == WalletType.monero || widget.type == WalletType.wownero);
Widget get expandIcon => Container( Widget get expandIcon =>
Container(
padding: EdgeInsets.all(18), padding: EdgeInsets.all(18),
width: 24, width: 24,
height: 24, height: 24,
child: Image.asset( child: Image.asset(
'assets/images/arrow_bottom_purple_icon.png', 'assets/images/arrow_bottom_purple_icon.png',
height: 8, height: 8,
color: Theme.of(context).hintColor, color: Theme
.of(context)
.hintColor,
), ),
); );
@ -285,8 +311,8 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
final setLang = isPolyseed final setLang = isPolyseed
? "POLYSEED_$language" ? "POLYSEED_$language"
: seedTypeController.value.text.contains("14") : seedTypeController.value.text.contains("14")
? "WOWSEED_" + language ? "WOWSEED_" + language
: language; : language;
setState(() { setState(() {
this.language = setLang; this.language = setLang;
seedWidgetStateKey.currentState!.changeSeedLanguage(setLang); seedWidgetStateKey.currentState!.changeSeedLanguage(setLang);
@ -298,24 +324,24 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
void _setLanguageLabel(String language) => void _setLanguageLabel(String language) =>
languageController.text = '${language.replaceAll("POLYSEED_", "")} (Seed language)'; languageController.text = '${language.replaceAll("POLYSEED_", "")} (Seed language)';
void _changeSeedType(SeedType item) { void _changeSeedType(MoneroSeedType item) {
_setSeedType(item); _setSeedType(item);
_changeLanguage('English'); _changeLanguage('English');
widget.seedTypeViewModel.setMoneroSeedType(item); widget.seedSettingsViewModel.setMoneroSeedType(item);
} }
void _setSeedType(SeedType item) { void _setSeedType(MoneroSeedType item) {
seedTypeController.text = item.toString(); seedTypeController.text = item.toString();
} }
List<SeedType> _getItems() { List<MoneroSeedType> _getItems() {
switch (widget.type) { switch (widget.type) {
case WalletType.monero: case WalletType.monero:
return [SeedType.legacy, SeedType.polyseed]; return [MoneroSeedType.legacy, MoneroSeedType.polyseed];
case WalletType.wownero: case WalletType.wownero:
return [SeedType.legacy, SeedType.polyseed, SeedType.wowneroSeed]; return [MoneroSeedType.legacy, MoneroSeedType.polyseed, MoneroSeedType.wowneroSeed];
default: default:
return [SeedType.legacy]; return [MoneroSeedType.legacy];
} }
} }
} }

View file

@ -12,7 +12,7 @@ import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -23,7 +23,7 @@ import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class WalletRestorePage extends BasePage { class WalletRestorePage extends BasePage {
WalletRestorePage(this.walletRestoreViewModel, this.seedTypeViewModel) WalletRestorePage(this.walletRestoreViewModel, this.seedSettingsViewModel)
: walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(), : walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(),
walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(), walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(),
_pages = [], _pages = [],
@ -33,7 +33,7 @@ class WalletRestorePage extends BasePage {
switch (mode) { switch (mode) {
case WalletRestoreMode.seed: case WalletRestoreMode.seed:
_pages.add(WalletRestoreFromSeedForm( _pages.add(WalletRestoreFromSeedForm(
seedTypeViewModel: seedTypeViewModel, seedSettingsViewModel: seedSettingsViewModel,
displayBlockHeightSelector: displayBlockHeightSelector:
walletRestoreViewModel.hasBlockchainHeightLanguageSelector, walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector, displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector,
@ -96,7 +96,7 @@ class WalletRestorePage extends BasePage {
)); ));
final WalletRestoreViewModel walletRestoreViewModel; final WalletRestoreViewModel walletRestoreViewModel;
final SeedTypeViewModel seedTypeViewModel; final SeedSettingsViewModel seedSettingsViewModel;
final PageController _controller; final PageController _controller;
final List<Widget> _pages; final List<Widget> _pages;
final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey; final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
@ -235,6 +235,7 @@ class WalletRestorePage extends BasePage {
onTap: () { onTap: () {
Navigator.of(context) Navigator.of(context)
.pushNamed(Routes.advancedPrivacySettings, arguments: { .pushNamed(Routes.advancedPrivacySettings, arguments: {
'isFromRestore': true,
'type': walletRestoreViewModel.type, 'type': walletRestoreViewModel.type,
'useTestnet': walletRestoreViewModel.useTestnet, 'useTestnet': walletRestoreViewModel.useTestnet,
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet 'toggleTestnet': walletRestoreViewModel.toggleUseTestnet
@ -324,8 +325,7 @@ class WalletRestorePage extends BasePage {
} }
if (walletRestoreViewModel.hasPassphrase) { if (walletRestoreViewModel.hasPassphrase) {
credentials['passphrase'] = credentials['passphrase'] = seedSettingsViewModel.passphrase;
walletRestoreFromSeedFormKey.currentState!.passphraseController.text;
} }
credentials['name'] = credentials['name'] =
@ -408,26 +408,19 @@ class WalletRestorePage extends BasePage {
) as DerivationInfo?; ) as DerivationInfo?;
} else if (derivationsWithHistory == 1) { } else if (derivationsWithHistory == 1) {
dInfo = derivations[derivationWithHistoryIndex]; dInfo = derivations[derivationWithHistoryIndex];
} } else if (derivations.length == 1) {
// get the default derivation for this wallet type:
if (dInfo == null) {
// we only return 1 derivation if we're pretty sure we know which one to use: // we only return 1 derivation if we're pretty sure we know which one to use:
if (derivations.length == 1) { dInfo = derivations.first;
dInfo = derivations.first; } else {
} else { // if we have multiple possible derivations, and none (or multiple) have histories
// if we have multiple possible derivations, and none have histories // we just default to the most common one:
// we just default to the most common one: dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
}
} }
this.derivationInfo = dInfo; this.derivationInfo = dInfo;
if (this.derivationInfo == null) {
this.derivationInfo = walletRestoreViewModel.getDefaultDerivation();
}
await walletRestoreViewModel.create(options: _credentials()); await walletRestoreViewModel.create(options: _credentials());
seedSettingsViewModel.setPassphrase(null);
} catch (e) { } catch (e) {
_formProcessing = false; _formProcessing = false;
rethrow; rethrow;

View file

@ -274,6 +274,7 @@ class SendPage extends BasePage {
? template.cryptoCurrency ? template.cryptoCurrency
: template.fiatCurrency, : template.fiatCurrency,
onTap: () async { onTap: () async {
sendViewModel.state = IsExecutingState();
if (template.additionalRecipients?.isNotEmpty ?? false) { if (template.additionalRecipients?.isNotEmpty ?? false) {
sendViewModel.clearOutputs(); sendViewModel.clearOutputs();
@ -302,6 +303,7 @@ class SendPage extends BasePage {
template: template, template: template,
); );
} }
sendViewModel.state = InitialExecutionState();
}, },
onRemove: () { onRemove: () {
showPopUp<void>( showPopUp<void>(
@ -373,6 +375,7 @@ class SendPage extends BasePage {
return LoadingPrimaryButton( return LoadingPrimaryButton(
key: ValueKey('send_page_send_button_key'), key: ValueKey('send_page_send_button_key'),
onPressed: () async { onPressed: () async {
if (sendViewModel.state is IsExecutingState) return;
if (_formKey.currentState != null && !_formKey.currentState!.validate()) { if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
if (sendViewModel.outputs.length > 1) { if (sendViewModel.outputs.length > 1) {
showErrorValidationAlert(context); showErrorValidationAlert(context);

View file

@ -6,7 +6,9 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -111,6 +113,33 @@ class PrivacyPage extends BasePage {
_privacySettingsViewModel.setUseTronGrid(value); _privacySettingsViewModel.setUseTronGrid(value);
}, },
), ),
if (_privacySettingsViewModel.canUseMempoolFeeAPI)
SettingsSwitcherCell(
title: S.current.live_fee_rates,
value: _privacySettingsViewModel.useMempoolFeeAPI,
onValueChange: (BuildContext _, bool isEnabled) async {
if (!isEnabled) {
final bool confirmation = await showPopUp<bool>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).warning,
alertContent: S.of(context).disable_fee_api_warning,
rightButtonText: S.of(context).confirm,
leftButtonText: S.of(context).cancel,
actionRightButton: () => Navigator.of(context).pop(true),
actionLeftButton: () => Navigator.of(context).pop(false));
}) ??
false;
if (confirmation) {
_privacySettingsViewModel.setUseMempoolFeeAPI(isEnabled);
}
return;
}
_privacySettingsViewModel.setUseMempoolFeeAPI(isEnabled);
},
),
SettingsCellWithArrow( SettingsCellWithArrow(
title: S.current.domain_looks_up, title: S.current.domain_looks_up,
handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage), handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage),

View file

@ -113,10 +113,6 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
if (item.isDisabled) {
return;
}
bool newValue = !item.value; bool newValue = !item.value;
item.value = newValue; item.value = newValue;
widget.onChanged(index, newValue); widget.onChanged(index, newValue);
@ -134,7 +130,7 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
borderColor: Theme.of(context).dividerColor, borderColor: Theme.of(context).dividerColor,
iconColor: Colors.white, iconColor: Colors.white,
onChanged: (bool? value) { onChanged: (bool? value) {
if (value == null || item.isDisabled) { if (value == null) {
return; return;
} }

View file

@ -11,36 +11,36 @@ class SeedLanguagePickerOption {
final String name; final String name;
final String nameLocalized; final String nameLocalized;
final Image image; final Image image;
final List<SeedType> supportedSeedTypes; final List<MoneroSeedType> supportedSeedTypes;
} }
final List<SeedLanguagePickerOption> seedLanguages = [ final List<SeedLanguagePickerOption> seedLanguages = [
SeedLanguagePickerOption('English', S.current.seed_language_english, SeedLanguagePickerOption('English', S.current.seed_language_english,
Image.asset('assets/images/flags/usa.png'), [SeedType.legacy, SeedType.polyseed]), Image.asset('assets/images/flags/usa.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Chinese (Simplified)', S.current.seed_language_chinese, SeedLanguagePickerOption('Chinese (Simplified)', S.current.seed_language_chinese,
Image.asset('assets/images/flags/chn.png'), [SeedType.legacy, SeedType.polyseed]), Image.asset('assets/images/flags/chn.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Chinese (Traditional)', S.current.seed_language_chinese_traditional, SeedLanguagePickerOption('Chinese (Traditional)', S.current.seed_language_chinese_traditional,
Image.asset('assets/images/flags/chn.png'), [SeedType.polyseed]), Image.asset('assets/images/flags/chn.png'), [MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Dutch', S.current.seed_language_dutch, SeedLanguagePickerOption('Dutch', S.current.seed_language_dutch,
Image.asset('assets/images/flags/nld.png'), [SeedType.legacy]), Image.asset('assets/images/flags/nld.png'), [MoneroSeedType.legacy]),
SeedLanguagePickerOption('German', S.current.seed_language_german, SeedLanguagePickerOption('German', S.current.seed_language_german,
Image.asset('assets/images/flags/deu.png'), [SeedType.legacy]), Image.asset('assets/images/flags/deu.png'), [MoneroSeedType.legacy]),
SeedLanguagePickerOption('Japanese', S.current.seed_language_japanese, SeedLanguagePickerOption('Japanese', S.current.seed_language_japanese,
Image.asset('assets/images/flags/jpn.png'), [SeedType.legacy, SeedType.polyseed]), Image.asset('assets/images/flags/jpn.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Korean', S.current.seed_language_korean, SeedLanguagePickerOption('Korean', S.current.seed_language_korean,
Image.asset('assets/images/flags/kor.png'), [SeedType.polyseed]), Image.asset('assets/images/flags/kor.png'), [MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Portuguese', S.current.seed_language_portuguese, SeedLanguagePickerOption('Portuguese', S.current.seed_language_portuguese,
Image.asset('assets/images/flags/prt.png'), [SeedType.legacy, SeedType.polyseed]), Image.asset('assets/images/flags/prt.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Russian', S.current.seed_language_russian, SeedLanguagePickerOption('Russian', S.current.seed_language_russian,
Image.asset('assets/images/flags/rus.png'), [SeedType.legacy]), Image.asset('assets/images/flags/rus.png'), [MoneroSeedType.legacy]),
SeedLanguagePickerOption('Czech', S.current.seed_language_czech, SeedLanguagePickerOption('Czech', S.current.seed_language_czech,
Image.asset('assets/images/flags/czk.png'), [SeedType.polyseed]), Image.asset('assets/images/flags/czk.png'), [MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Spanish', S.current.seed_language_spanish, SeedLanguagePickerOption('Spanish', S.current.seed_language_spanish,
Image.asset('assets/images/flags/esp.png'), [SeedType.legacy, SeedType.polyseed]), Image.asset('assets/images/flags/esp.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
SeedLanguagePickerOption('French', S.current.seed_language_french, SeedLanguagePickerOption('French', S.current.seed_language_french,
Image.asset('assets/images/flags/fra.png'), [SeedType.legacy, SeedType.polyseed]), Image.asset('assets/images/flags/fra.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
SeedLanguagePickerOption('Italian', S.current.seed_language_italian, SeedLanguagePickerOption('Italian', S.current.seed_language_italian,
Image.asset('assets/images/flags/ita.png'), [SeedType.legacy, SeedType.polyseed]), Image.asset('assets/images/flags/ita.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
]; ];
const defaultSeedLanguage = 'English'; const defaultSeedLanguage = 'English';
@ -51,11 +51,11 @@ class SeedLanguagePicker extends StatefulWidget {
SeedLanguagePicker( SeedLanguagePicker(
{Key? key, {Key? key,
this.selected = defaultSeedLanguage, this.selected = defaultSeedLanguage,
this.seedType = SeedType.defaultSeedType, this.seedType = MoneroSeedType.defaultSeedType,
required this.onItemSelected}) required this.onItemSelected})
: super(key: key); : super(key: key);
final SeedType seedType; final MoneroSeedType seedType;
final String selected; final String selected;
final Function(String) onItemSelected; final Function(String) onItemSelected;
@ -68,7 +68,7 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
SeedLanguagePickerState( SeedLanguagePickerState(
{required this.selected, required this.onItemSelected, required this.seedType}); {required this.selected, required this.onItemSelected, required this.seedType});
final SeedType seedType; final MoneroSeedType seedType;
final String selected; final String selected;
final Function(String) onItemSelected; final Function(String) onItemSelected;

View file

@ -8,13 +8,13 @@ import 'package:flutter/material.dart';
class SeedLanguageSelector extends StatefulWidget { class SeedLanguageSelector extends StatefulWidget {
SeedLanguageSelector({ SeedLanguageSelector({
required this.initialSelected, required this.initialSelected,
this.seedType = SeedType.defaultSeedType, this.seedType = MoneroSeedType.defaultSeedType,
this.buttonKey, this.buttonKey,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final String initialSelected; final String initialSelected;
final SeedType seedType; final MoneroSeedType seedType;
final Key? buttonKey; final Key? buttonKey;
@override @override

View file

@ -16,7 +16,9 @@ abstract class TradeFilterStoreBase with Store {
displaySimpleSwap = true, displaySimpleSwap = true,
displayTrocador = true, displayTrocador = true,
displayExolix = true, displayExolix = true,
displayThorChain = true; displayThorChain = true,
displayLetsExchange = true,
displayStealthEx = true;
@observable @observable
bool displayXMRTO; bool displayXMRTO;
@ -42,6 +44,12 @@ abstract class TradeFilterStoreBase with Store {
@observable @observable
bool displayThorChain; bool displayThorChain;
@observable
bool displayLetsExchange;
@observable
bool displayStealthEx;
@computed @computed
bool get displayAllTrades => bool get displayAllTrades =>
displayChangeNow && displayChangeNow &&
@ -49,7 +57,9 @@ abstract class TradeFilterStoreBase with Store {
displaySimpleSwap && displaySimpleSwap &&
displayTrocador && displayTrocador &&
displayExolix && displayExolix &&
displayThorChain; displayThorChain &&
displayLetsExchange &&
displayStealthEx;
@action @action
void toggleDisplayExchange(ExchangeProviderDescription provider) { void toggleDisplayExchange(ExchangeProviderDescription provider) {
@ -78,6 +88,11 @@ abstract class TradeFilterStoreBase with Store {
case ExchangeProviderDescription.thorChain: case ExchangeProviderDescription.thorChain:
displayThorChain = !displayThorChain; displayThorChain = !displayThorChain;
break; break;
case ExchangeProviderDescription.letsExchange:
displayLetsExchange = !displayLetsExchange;
case ExchangeProviderDescription.stealthEx:
displayStealthEx = !displayStealthEx;
break;
case ExchangeProviderDescription.all: case ExchangeProviderDescription.all:
if (displayAllTrades) { if (displayAllTrades) {
displayChangeNow = false; displayChangeNow = false;
@ -88,6 +103,8 @@ abstract class TradeFilterStoreBase with Store {
displayTrocador = false; displayTrocador = false;
displayExolix = false; displayExolix = false;
displayThorChain = false; displayThorChain = false;
displayLetsExchange = false;
displayStealthEx = false;
} else { } else {
displayChangeNow = true; displayChangeNow = true;
displaySideShift = true; displaySideShift = true;
@ -97,6 +114,8 @@ abstract class TradeFilterStoreBase with Store {
displayTrocador = true; displayTrocador = true;
displayExolix = true; displayExolix = true;
displayThorChain = true; displayThorChain = true;
displayLetsExchange = true;
displayStealthEx = true;
} }
break; break;
} }
@ -112,13 +131,21 @@ abstract class TradeFilterStoreBase with Store {
? _trades ? _trades
.where((item) => .where((item) =>
(displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) || (displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) ||
(displaySideShift && item.trade.provider == ExchangeProviderDescription.sideShift) || (displaySideShift &&
(displayChangeNow && item.trade.provider == ExchangeProviderDescription.changeNow) || item.trade.provider == ExchangeProviderDescription.sideShift) ||
(displayMorphToken && item.trade.provider == ExchangeProviderDescription.morphToken) || (displayChangeNow &&
(displaySimpleSwap && item.trade.provider == ExchangeProviderDescription.simpleSwap) || item.trade.provider == ExchangeProviderDescription.changeNow) ||
(displayMorphToken &&
item.trade.provider == ExchangeProviderDescription.morphToken) ||
(displaySimpleSwap &&
item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) || (displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) ||
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix) || (displayExolix && item.trade.provider == ExchangeProviderDescription.exolix) ||
(displayThorChain && item.trade.provider == ExchangeProviderDescription.thorChain)) (displayThorChain &&
item.trade.provider == ExchangeProviderDescription.thorChain) ||
(displayLetsExchange &&
item.trade.provider == ExchangeProviderDescription.letsExchange) ||
(displayStealthEx && item.trade.provider == ExchangeProviderDescription.stealthEx))
.toList() .toList()
: _trades; : _trades;
} }

View file

@ -0,0 +1,11 @@
import 'package:mobx/mobx.dart';
part 'seed_settings_store.g.dart';
class SeedSettingsStore = SeedSettingsStoreBase with _$SeedSettingsStore;
abstract class SeedSettingsStoreBase with Store {
@observable
String? passphrase;
}

View file

@ -1,45 +1,46 @@
import 'dart:io'; import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/core/secure_storage.dart'; import 'package:cake_wallet/core/secure_storage.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/action_list_display_mode.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/provider_types.dart';
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/entities/background_tasks.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/entities/pin_code_required_duration.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/provider_types.dart';
import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/entities/seed_phrase_length.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart';
import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/entities/wallet_list_order_types.dart'; import 'package:cake_wallet/entities/wallet_list_order_types.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/package_info.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/set_app_secure_native.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/utils/package_info.dart';
import 'package:cake_wallet/di.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/node.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/entities/action_list_display_mode.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cw_core/set_app_secure_native.dart';
part 'settings_store.g.dart'; part 'settings_store.g.dart';
@ -55,7 +56,8 @@ abstract class SettingsStoreBase with Store {
required BalanceDisplayMode initialBalanceDisplayMode, required BalanceDisplayMode initialBalanceDisplayMode,
required bool initialSaveRecipientAddress, required bool initialSaveRecipientAddress,
required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus, required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus,
required SeedType initialMoneroSeedType, required MoneroSeedType initialMoneroSeedType,
required BitcoinSeedType initialBitcoinSeedType,
required bool initialAppSecure, required bool initialAppSecure,
required bool initialDisableBuy, required bool initialDisableBuy,
required bool initialDisableSell, required bool initialDisableSell,
@ -99,6 +101,7 @@ abstract class SettingsStoreBase with Store {
required this.useEtherscan, required this.useEtherscan,
required this.usePolygonScan, required this.usePolygonScan,
required this.useTronGrid, required this.useTronGrid,
required this.useMempoolFeeAPI,
required this.defaultNanoRep, required this.defaultNanoRep,
required this.defaultBananoRep, required this.defaultBananoRep,
required this.lookupsTwitter, required this.lookupsTwitter,
@ -128,6 +131,7 @@ abstract class SettingsStoreBase with Store {
shouldSaveRecipientAddress = initialSaveRecipientAddress, shouldSaveRecipientAddress = initialSaveRecipientAddress,
autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus, autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus,
moneroSeedType = initialMoneroSeedType, moneroSeedType = initialMoneroSeedType,
bitcoinSeedType = initialBitcoinSeedType,
fiatApiMode = initialFiatMode, fiatApiMode = initialFiatMode,
allowBiometricalAuthentication = initialAllowBiometricalAuthentication, allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
selectedCake2FAPreset = initialCake2FAPresetOptions, selectedCake2FAPreset = initialCake2FAPresetOptions,
@ -329,9 +333,14 @@ abstract class SettingsStoreBase with Store {
reaction( reaction(
(_) => moneroSeedType, (_) => moneroSeedType,
(SeedType moneroSeedType) => (MoneroSeedType moneroSeedType) =>
sharedPreferences.setInt(PreferencesKey.moneroSeedType, moneroSeedType.raw)); sharedPreferences.setInt(PreferencesKey.moneroSeedType, moneroSeedType.raw));
reaction(
(_) => bitcoinSeedType,
(BitcoinSeedType bitcoinSeedType) => sharedPreferences.setInt(
PreferencesKey.bitcoinSeedType, bitcoinSeedType.raw));
reaction( reaction(
(_) => fiatApiMode, (_) => fiatApiMode,
(FiatApiMode mode) => (FiatApiMode mode) =>
@ -408,6 +417,9 @@ abstract class SettingsStoreBase with Store {
reaction((_) => useTronGrid, reaction((_) => useTronGrid,
(bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid)); (bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid));
reaction((_) => useMempoolFeeAPI,
(bool useMempoolFeeAPI) => _sharedPreferences.setBool(PreferencesKey.useMempoolFeeAPI, useMempoolFeeAPI));
reaction((_) => defaultNanoRep, reaction((_) => defaultNanoRep,
(String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep)); (String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep));
@ -555,7 +567,8 @@ abstract class SettingsStoreBase with Store {
static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized; static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized;
static final walletPasswordDirectInput = Platform.isLinux; static final walletPasswordDirectInput = Platform.isLinux;
static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords; static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords;
static const defaultMoneroSeedType = SeedType.defaultSeedType; static const defaultMoneroSeedType = MoneroSeedType.defaultSeedType;
static const defaultBitcoinSeedType = BitcoinSeedType.defaultDerivationType;
@observable @observable
FiatCurrency fiatCurrency; FiatCurrency fiatCurrency;
@ -585,7 +598,10 @@ abstract class SettingsStoreBase with Store {
AutoGenerateSubaddressStatus autoGenerateSubaddressStatus; AutoGenerateSubaddressStatus autoGenerateSubaddressStatus;
@observable @observable
SeedType moneroSeedType; MoneroSeedType moneroSeedType;
@observable
BitcoinSeedType bitcoinSeedType;
@observable @observable
bool isAppSecure; bool isAppSecure;
@ -695,6 +711,9 @@ abstract class SettingsStoreBase with Store {
@observable @observable
bool useTronGrid; bool useTronGrid;
@observable
bool useMempoolFeeAPI;
@observable @observable
String defaultNanoRep; String defaultNanoRep;
@ -880,6 +899,7 @@ abstract class SettingsStoreBase with Store {
final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true; final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true; final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
final useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true; final useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
final useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? ""; final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
final defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? ""; final defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? "";
final lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true; final lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true;
@ -945,9 +965,15 @@ abstract class SettingsStoreBase with Store {
final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType); final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType);
final moneroSeedType = _moneroSeedType != null final moneroSeedType = _moneroSeedType != null
? SeedType.deserialize(raw: _moneroSeedType) ? MoneroSeedType.deserialize(raw: _moneroSeedType)
: defaultMoneroSeedType; : defaultMoneroSeedType;
final _bitcoinSeedType = sharedPreferences.getInt(PreferencesKey.bitcoinSeedType);
final bitcoinSeedType = _bitcoinSeedType != null
? BitcoinSeedType.deserialize(raw: _bitcoinSeedType)
: defaultBitcoinSeedType;
final nodes = <WalletType, Node>{}; final nodes = <WalletType, Node>{};
final powNodes = <WalletType, Node>{}; final powNodes = <WalletType, Node>{};
@ -1111,6 +1137,7 @@ abstract class SettingsStoreBase with Store {
initialSaveRecipientAddress: shouldSaveRecipientAddress, initialSaveRecipientAddress: shouldSaveRecipientAddress,
initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus, initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus,
initialMoneroSeedType: moneroSeedType, initialMoneroSeedType: moneroSeedType,
initialBitcoinSeedType: bitcoinSeedType,
initialAppSecure: isAppSecure, initialAppSecure: isAppSecure,
initialDisableBuy: disableBuy, initialDisableBuy: disableBuy,
initialDisableSell: disableSell, initialDisableSell: disableSell,
@ -1135,6 +1162,7 @@ abstract class SettingsStoreBase with Store {
useEtherscan: useEtherscan, useEtherscan: useEtherscan,
usePolygonScan: usePolygonScan, usePolygonScan: usePolygonScan,
useTronGrid: useTronGrid, useTronGrid: useTronGrid,
useMempoolFeeAPI: useMempoolFeeAPI,
defaultNanoRep: defaultNanoRep, defaultNanoRep: defaultNanoRep,
defaultBananoRep: defaultBananoRep, defaultBananoRep: defaultBananoRep,
lookupsTwitter: lookupsTwitter, lookupsTwitter: lookupsTwitter,
@ -1233,9 +1261,15 @@ abstract class SettingsStoreBase with Store {
final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType); final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType);
moneroSeedType = _moneroSeedType != null moneroSeedType = _moneroSeedType != null
? SeedType.deserialize(raw: _moneroSeedType) ? MoneroSeedType.deserialize(raw: _moneroSeedType)
: defaultMoneroSeedType; : defaultMoneroSeedType;
final _bitcoinSeedType = sharedPreferences.getInt(PreferencesKey.bitcoinSeedType);
bitcoinSeedType = _bitcoinSeedType != null
? BitcoinSeedType.deserialize(raw: _bitcoinSeedType)
: defaultBitcoinSeedType;
balanceDisplayMode = BalanceDisplayMode.deserialize( balanceDisplayMode = BalanceDisplayMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!); raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
shouldSaveRecipientAddress = shouldSaveRecipientAddress =
@ -1282,6 +1316,7 @@ abstract class SettingsStoreBase with Store {
useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true; useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true;
usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true; usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true;
useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true; useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true;
useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true;
defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? ""; defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? "";
defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? ""; defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? "";
lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true; lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true;

View file

@ -172,8 +172,14 @@ class ExceptionHandler {
"Error while launching http", "Error while launching http",
"OS Error: Network is unreachable", "OS Error: Network is unreachable",
"ClientException: Write failed, uri=http", "ClientException: Write failed, uri=http",
"Connection terminated during handshake",
"Corrupted wallets seeds", "Corrupted wallets seeds",
"bad_alloc",
"does not correspond",
"basic_string",
"input_stream",
"input stream error",
"invalid signature",
"invalid password",
]; ];
static Future<void> _addDeviceInfo(File file) async { static Future<void> _addDeviceInfo(File file) async {

View file

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class ImageUtil {
static Widget getImageFromPath({required String imagePath, double? height, double? width}) {
final bool isNetworkImage = imagePath.startsWith('http') || imagePath.startsWith('https');
final bool isSvg = imagePath.endsWith('.svg');
final double _height = height ?? 35;
final double _width = width ?? 35;
if (isNetworkImage) {
return isSvg
? SvgPicture.network(
imagePath,
height: _height,
width: _width,
placeholderBuilder: (BuildContext context) => Container(
height: _height,
width: _width,
child: Center(
child: CircularProgressIndicator(),
),
),
)
: Image.network(
imagePath,
height: _height,
width: _width,
loadingBuilder:
(BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Container(
height: _height,
width: _width,
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return Container(
height: _height,
width: _width,
);
},
);
} else {
return isSvg
? SvgPicture.asset(imagePath, height: _height, width: _width)
: Image.asset(imagePath, height: _height, width: _width);
}
}
}

View file

@ -30,6 +30,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
@computed
bool get hasSeedPhraseLengthOption { bool get hasSeedPhraseLengthOption {
// convert to switch case so that it give a syntax error when adding a new wallet type // convert to switch case so that it give a syntax error when adding a new wallet type
// thus we don't forget about it // thus we don't forget about it
@ -40,11 +41,14 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
case WalletType.solana: case WalletType.solana:
case WalletType.tron: case WalletType.tron:
return true; return true;
case WalletType.bitcoin:
case WalletType.litecoin:
return _settingsStore.bitcoinSeedType == BitcoinSeedType.bip39;
case WalletType.monero: case WalletType.monero:
case WalletType.wownero: case WalletType.wownero:
case WalletType.none: case WalletType.none:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.haven: case WalletType.haven:
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
@ -52,7 +56,13 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
} }
} }
bool get hasSeedTypeOption => type == WalletType.monero || type == WalletType.wownero; bool get hasSeedTypeOption => [WalletType.monero, WalletType.wownero].contains(type);
bool get hasPassphraseOption => [
WalletType.bitcoin,
WalletType.litecoin,
WalletType.bitcoinCash,
].contains(type);
@computed @computed
bool get addCustomNode => _addCustomNode; bool get addCustomNode => _addCustomNode;
@ -61,7 +71,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
SeedPhraseLength get seedPhraseLength => _settingsStore.seedPhraseLength; SeedPhraseLength get seedPhraseLength => _settingsStore.seedPhraseLength;
@computed @computed
bool get isPolySeed => _settingsStore.moneroSeedType == SeedType.polyseed; bool get isPolySeed => _settingsStore.moneroSeedType == MoneroSeedType.polyseed;
@action @action
void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode; void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode;

View file

@ -1,12 +1,14 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/entities/provider_types.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/service_status.dart'; import 'package:cake_wallet/entities/service_status.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -45,11 +47,9 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:eth_sig_util/util/utils.dart'; import 'package:eth_sig_util/util/utils.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
part 'dashboard_view_model.g.dart'; part 'dashboard_view_model.g.dart';
@ -129,6 +129,16 @@ abstract class DashboardViewModelBase with Store {
caption: ExchangeProviderDescription.thorChain.title, caption: ExchangeProviderDescription.thorChain.title,
onChanged: () => onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)), tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.thorChain)),
FilterItem(
value: () => tradeFilterStore.displayLetsExchange,
caption: ExchangeProviderDescription.letsExchange.title,
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)),
FilterItem(
value: () => tradeFilterStore.displayStealthEx,
caption: ExchangeProviderDescription.stealthEx.title,
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.stealthEx)),
] ]
}, },
subname = '', subname = '',

View file

@ -69,12 +69,10 @@ class TransactionListItem extends ActionListItem with Keyable {
} }
String get formattedStatus { String get formattedStatus {
if (transaction.direction == TransactionDirection.incoming) { if (balanceViewModel.wallet.type == WalletType.monero ||
if (balanceViewModel.wallet.type == WalletType.monero || balanceViewModel.wallet.type == WalletType.wownero ||
balanceViewModel.wallet.type == WalletType.wownero || balanceViewModel.wallet.type == WalletType.haven) {
balanceViewModel.wallet.type == WalletType.haven) { return formattedPendingStatus;
return formattedPendingStatus;
}
} }
return transaction.isPending ? S.current.pending : ''; return transaction.isPending ? S.current.pending : '';
} }

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart'; import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
@ -52,6 +53,8 @@ abstract class ExchangeTradeViewModelBase with Store {
case ExchangeProviderDescription.quantex: case ExchangeProviderDescription.quantex:
_provider = QuantexExchangeProvider(); _provider = QuantexExchangeProvider();
break; break;
case ExchangeProviderDescription.stealthEx:
_provider = StealthExExchangeProvider();
case ExchangeProviderDescription.thorChain: case ExchangeProviderDescription.thorChain:
_provider = ThorChainExchangeProvider(tradesStore: trades); _provider = ThorChainExchangeProvider(tradesStore: trades);
break; break;

View file

@ -4,6 +4,8 @@ import 'dart:convert';
import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cake_wallet/core/create_trade_result.dart'; import 'package:cake_wallet/core/create_trade_result.dart';
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/sync_status.dart'; import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
@ -160,15 +162,17 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final SharedPreferences sharedPreferences; final SharedPreferences sharedPreferences;
List<ExchangeProvider> get _allProviders => [ List<ExchangeProvider> get _allProviders => [
ChangeNowExchangeProvider(settingsStore: _settingsStore), ChangeNowExchangeProvider(settingsStore: _settingsStore),
SideShiftExchangeProvider(), SideShiftExchangeProvider(),
SimpleSwapExchangeProvider(), SimpleSwapExchangeProvider(),
ThorChainExchangeProvider(tradesStore: trades), ThorChainExchangeProvider(tradesStore: trades),
if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(), if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
QuantexExchangeProvider(), QuantexExchangeProvider(),
TrocadorExchangeProvider( LetsExchangeExchangeProvider(),
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates), StealthExExchangeProvider(),
]; TrocadorExchangeProvider(
useTorOnly: _useTorOnly, providerStates: _settingsStore.trocadorProviderStates),
];
@observable @observable
ExchangeProvider? provider; ExchangeProvider? provider;

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/wownero/wownero.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -26,13 +27,13 @@ class WalletRestorationFromQRVM = WalletRestorationFromQRVMBase with _$WalletRes
abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store { abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store {
WalletRestorationFromQRVMBase(AppStore appStore, WalletCreationService walletCreationService, WalletRestorationFromQRVMBase(AppStore appStore, WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource, WalletType type) Box<WalletInfo> walletInfoSource, WalletType type, SeedSettingsViewModel seedSettingsViewModel)
: height = 0, : height = 0,
viewKey = '', viewKey = '',
spendKey = '', spendKey = '',
wif = '', wif = '',
address = '', address = '',
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true); super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true);
@observable @observable
int height; int height;
@ -61,7 +62,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
derivationInfo = options["derivationInfo"] as DerivationInfo?; derivationInfo = options["derivationInfo"] as DerivationInfo?;
passphrase = options["passphrase"] as String?; passphrase = options["passphrase"] as String?;
} }
derivationInfo ??= getDefaultDerivation(); derivationInfo ??= getDefaultCreateDerivation();
switch (restoreWallet.restoreMode) { switch (restoreWallet.restoreMode) {
case WalletRestoreMode.keys: case WalletRestoreMode.keys:

View file

@ -46,7 +46,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
final data = await file.readAsBytes(); final data = await file.readAsBytes();
await backupService.importBackup(data, password); await backupService.importBackup(data, password);
await main(); await initializeAppAtRoot(reInitializing: true);
final store = getIt.get<AppStore>(); final store = getIt.get<AppStore>();
ReactionDisposer? reaction; ReactionDisposer? reaction;

View file

@ -0,0 +1,34 @@
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/seed_settings_store.dart';
import 'package:mobx/mobx.dart';
part 'seed_settings_view_model.g.dart';
class SeedSettingsViewModel = SeedSettingsViewModelBase with _$SeedSettingsViewModel;
abstract class SeedSettingsViewModelBase with Store {
SeedSettingsViewModelBase(this._appStore, this._seedSettingsStore);
@computed
MoneroSeedType get moneroSeedType => _appStore.settingsStore.moneroSeedType;
@action
void setMoneroSeedType(MoneroSeedType seedType) => _appStore.settingsStore.moneroSeedType = seedType;
@computed
BitcoinSeedType get bitcoinSeedType => _appStore.settingsStore.bitcoinSeedType;
@action
void setBitcoinSeedType(BitcoinSeedType derivationType) =>
_appStore.settingsStore.bitcoinSeedType = derivationType;
@computed
String? get passphrase => this._seedSettingsStore.passphrase;
@action
void setPassphrase(String? passphrase) => this._seedSettingsStore.passphrase = passphrase;
final AppStore _appStore;
final SeedSettingsStore _seedSettingsStore;
}

View file

@ -1,19 +0,0 @@
import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:mobx/mobx.dart';
part 'seed_type_view_model.g.dart';
class SeedTypeViewModel = SeedTypeViewModelBase with _$SeedTypeViewModel;
abstract class SeedTypeViewModelBase with Store {
SeedTypeViewModelBase(this._appStore);
@computed
SeedType get moneroSeedType => _appStore.settingsStore.moneroSeedType;
@action
void setMoneroSeedType(SeedType seedType) => _appStore.settingsStore.moneroSeedType = seedType;
final AppStore _appStore;
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
@ -75,6 +76,9 @@ abstract class PrivacySettingsViewModelBase with Store {
@computed @computed
bool get useTronGrid => _settingsStore.useTronGrid; bool get useTronGrid => _settingsStore.useTronGrid;
@computed
bool get useMempoolFeeAPI => _settingsStore.useMempoolFeeAPI;
@computed @computed
bool get lookupTwitter => _settingsStore.lookupsTwitter; bool get lookupTwitter => _settingsStore.lookupsTwitter;
@ -99,6 +103,8 @@ abstract class PrivacySettingsViewModelBase with Store {
bool get canUseTronGrid => _wallet.type == WalletType.tron; bool get canUseTronGrid => _wallet.type == WalletType.tron;
bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin;
@action @action
void setShouldSaveRecipientAddress(bool value) => void setShouldSaveRecipientAddress(bool value) =>
_settingsStore.shouldSaveRecipientAddress = value; _settingsStore.shouldSaveRecipientAddress = value;
@ -156,4 +162,9 @@ abstract class PrivacySettingsViewModelBase with Store {
_settingsStore.useTronGrid = value; _settingsStore.useTronGrid = value;
tron!.updateTronGridUsageState(_wallet, value); tron!.updateTronGridUsageState(_wallet, value);
} }
@action
void setUseMempoolFeeAPI(bool value) {
_settingsStore.useMempoolFeeAPI = value;
}
} }

View file

@ -4,9 +4,11 @@ import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/quantex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart'; import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
@ -59,6 +61,11 @@ abstract class TradeDetailsViewModelBase with Store {
break; break;
case ExchangeProviderDescription.quantex: case ExchangeProviderDescription.quantex:
_provider = QuantexExchangeProvider(); _provider = QuantexExchangeProvider();
case ExchangeProviderDescription.letsExchange:
_provider = LetsExchangeExchangeProvider();
break;
case ExchangeProviderDescription.stealthEx:
_provider = StealthExExchangeProvider();
break; break;
} }
@ -86,6 +93,10 @@ abstract class TradeDetailsViewModelBase with Store {
return 'https://track.ninerealms.com/${trade.id}'; return 'https://track.ninerealms.com/${trade.id}';
case ExchangeProviderDescription.quantex: case ExchangeProviderDescription.quantex:
return 'https://myquantex.com/send/${trade.id}'; return 'https://myquantex.com/send/${trade.id}';
case ExchangeProviderDescription.letsExchange:
return 'https://letsexchange.io/?transactionId=${trade.id}';
case ExchangeProviderDescription.stealthEx:
return 'https://stealthex.io/exchange/?id=${trade.id}';
} }
return null; return null;
} }

View file

@ -1,20 +1,21 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/entities/background_tasks.dart';
import 'package:cake_wallet/entities/generate_name.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:hive/hive.dart'; import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:mobx/mobx.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:hive/hive.dart';
import 'package:cake_wallet/entities/generate_name.dart'; import 'package:mobx/mobx.dart';
import 'package:polyseed/polyseed.dart'; import 'package:polyseed/polyseed.dart';
part 'wallet_creation_vm.g.dart'; part 'wallet_creation_vm.g.dart';
@ -23,6 +24,7 @@ class WalletCreationVM = WalletCreationVMBase with _$WalletCreationVM;
abstract class WalletCreationVMBase with Store { abstract class WalletCreationVMBase with Store {
WalletCreationVMBase(this._appStore, this._walletInfoSource, this.walletCreationService, WalletCreationVMBase(this._appStore, this._walletInfoSource, this.walletCreationService,
this.seedSettingsViewModel,
{required this.type, required this.isRecovery}) {required this.type, required this.isRecovery})
: state = InitialExecutionState(), : state = InitialExecutionState(),
name = ''; name = '';
@ -44,7 +46,6 @@ abstract class WalletCreationVMBase with Store {
@observable @observable
String? repeatedWalletPassword; String? repeatedWalletPassword;
bool get hasWalletPassword => SettingsStoreBase.walletPasswordDirectInput; bool get hasWalletPassword => SettingsStoreBase.walletPasswordDirectInput;
WalletType type; WalletType type;
@ -52,6 +53,7 @@ abstract class WalletCreationVMBase with Store {
final WalletCreationService walletCreationService; final WalletCreationService walletCreationService;
final Box<WalletInfo> _walletInfoSource; final Box<WalletInfo> _walletInfoSource;
final AppStore _appStore; final AppStore _appStore;
final SeedSettingsViewModel seedSettingsViewModel;
bool isPolyseed(String seed) => bool isPolyseed(String seed) =>
(type == WalletType.monero || type == WalletType.wownero) && (type == WalletType.monero || type == WalletType.wownero) &&
@ -95,7 +97,7 @@ abstract class WalletCreationVMBase with Store {
dirPath: dirPath, dirPath: dirPath,
address: '', address: '',
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven, showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(), derivationInfo: credentials.derivationInfo ?? getDefaultCreateDerivation(),
hardwareWalletType: credentials.hardwareWalletType, hardwareWalletType: credentials.hardwareWalletType,
); );
@ -109,17 +111,35 @@ abstract class WalletCreationVMBase with Store {
getIt.get<BackgroundTasks>().registerSyncTask(); getIt.get<BackgroundTasks>().registerSyncTask();
_appStore.authenticationStore.allowed(); _appStore.authenticationStore.allowed();
state = ExecutedSuccessfullyState(); state = ExecutedSuccessfullyState();
} catch (e, s) { } catch (e, _) {
state = FailureState(e.toString()); state = FailureState(e.toString());
} }
} }
DerivationInfo? getDefaultDerivation() { DerivationInfo? getDefaultCreateDerivation() {
switch (this.type) { final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
switch (type) {
case WalletType.nano: case WalletType.nano:
return DerivationInfo(derivationType: DerivationType.nano); return DerivationInfo(derivationType: DerivationType.nano);
case WalletType.bitcoin: case WalletType.bitcoin:
if (useBip39) {
return DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/0'",
description: "Standard BIP84 native segwit",
scriptType: "p2wpkh",
);
}
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
case WalletType.litecoin: case WalletType.litecoin:
if (useBip39) {
return DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/84'/2'/0'",
description: "Default Litecoin",
scriptType: "p2wpkh",
);
}
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first; return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
default: default:
return null; return null;
@ -127,10 +147,14 @@ abstract class WalletCreationVMBase with Store {
} }
DerivationInfo? getCommonRestoreDerivation() { DerivationInfo? getCommonRestoreDerivation() {
final useElectrum = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.electrum;
switch (this.type) { switch (this.type) {
case WalletType.nano: case WalletType.nano:
return DerivationInfo(derivationType: DerivationType.nano); return DerivationInfo(derivationType: DerivationType.nano);
case WalletType.bitcoin: case WalletType.bitcoin:
if (useElectrum) {
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
}
return DerivationInfo( return DerivationInfo(
derivationType: DerivationType.bip39, derivationType: DerivationType.bip39,
derivationPath: "m/84'/0'/0'/0", derivationPath: "m/84'/0'/0'/0",
@ -138,6 +162,9 @@ abstract class WalletCreationVMBase with Store {
scriptType: "p2wpkh", scriptType: "p2wpkh",
); );
case WalletType.litecoin: case WalletType.litecoin:
if (useElectrum) {
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
}
return DerivationInfo( return DerivationInfo(
derivationType: DerivationType.bip39, derivationType: DerivationType.bip39,
derivationPath: "m/84'/2'/0'/0", derivationPath: "m/84'/2'/0'/0",

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
import 'package:cw_core/hardware/hardware_account_data.dart'; import 'package:cw_core/hardware/hardware_account_data.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
@ -25,10 +26,15 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
int _nextIndex = 0; int _nextIndex = 0;
WalletHardwareRestoreViewModelBase(this.ledgerViewModel, AppStore appStore, WalletHardwareRestoreViewModelBase(
WalletCreationService walletCreationService, Box<WalletInfo> walletInfoSource, this.ledgerViewModel,
AppStore appStore,
WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource,
SeedSettingsViewModel seedSettingsViewModel,
{required WalletType type}) {required WalletType type})
: super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true); : super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
type: type, isRecovery: true);
@observable @observable
String name = ""; String name = "";

View file

@ -342,7 +342,8 @@ abstract class WalletKeysViewModelBase with Store {
'hexSeed': _appStore.wallet!.hexSeed!, 'hexSeed': _appStore.wallet!.hexSeed!,
if (_appStore.wallet!.seed == null && _appStore.wallet!.privateKey != null) if (_appStore.wallet!.seed == null && _appStore.wallet!.privateKey != null)
'private_key': _appStore.wallet!.privateKey!, 'private_key': _appStore.wallet!.privateKey!,
if (restoreHeightResult != null) ...{'height': restoreHeightResult} if (restoreHeightResult != null) ...{'height': restoreHeightResult},
if (_appStore.wallet!.passphrase != null) 'passphrase': _appStore.wallet!.passphrase!
}; };
} }

View file

@ -1,35 +1,42 @@
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/entities/seed_type.dart';
import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:hive/hive.dart'; import 'package:cake_wallet/haven/haven.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; import 'package:hive/hive.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'advanced_privacy_settings_view_model.dart';
import '../polygon/polygon.dart'; import '../polygon/polygon.dart';
import 'advanced_privacy_settings_view_model.dart';
part 'wallet_new_vm.g.dart'; part 'wallet_new_vm.g.dart';
class WalletNewVM = WalletNewVMBase with _$WalletNewVM; class WalletNewVM = WalletNewVMBase with _$WalletNewVM;
abstract class WalletNewVMBase extends WalletCreationVM with Store { abstract class WalletNewVMBase extends WalletCreationVM with Store {
WalletNewVMBase(AppStore appStore, WalletCreationService walletCreationService, WalletNewVMBase(
Box<WalletInfo> walletInfoSource, this.advancedPrivacySettingsViewModel, AppStore appStore,
WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource,
this.advancedPrivacySettingsViewModel,
SeedSettingsViewModel seedSettingsViewModel,
{required WalletType type}) {required WalletType type})
: selectedMnemonicLanguage = '', : selectedMnemonicLanguage = '',
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: false); super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
type: type, isRecovery: false);
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel; final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
@ -37,47 +44,58 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
String selectedMnemonicLanguage; String selectedMnemonicLanguage;
bool get hasLanguageSelector => bool get hasLanguageSelector =>
type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero; [WalletType.monero, WalletType.haven, WalletType.wownero].contains(type);
int get seedPhraseWordsLength { int get seedPhraseWordsLength {
switch (type) { switch (type) {
case WalletType.monero: case WalletType.monero:
case WalletType.wownero: case WalletType.wownero:
if (advancedPrivacySettingsViewModel.isPolySeed) { return advancedPrivacySettingsViewModel.isPolySeed ? 16 : 25;
return 16;
}
return 25;
case WalletType.tron: case WalletType.tron:
case WalletType.solana: case WalletType.solana:
case WalletType.polygon: case WalletType.polygon:
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return advancedPrivacySettingsViewModel.seedPhraseLength.value; return advancedPrivacySettingsViewModel.seedPhraseLength.value;
case WalletType.bitcoin:
case WalletType.litecoin:
return seedSettingsViewModel.bitcoinSeedType == BitcoinSeedType.bip39
? advancedPrivacySettingsViewModel.seedPhraseLength.value
: 24;
default: default:
return 24; return 24;
} }
} }
bool get hasSeedType => type == WalletType.monero || type == WalletType.wownero; bool get hasSeedType => [WalletType.monero, WalletType.wownero].contains(type);
@override @override
WalletCredentials getCredentials(dynamic _options) { WalletCredentials getCredentials(dynamic _options) {
final options = _options as List<dynamic>?; final options = _options as List<dynamic>?;
final passphrase = seedSettingsViewModel.passphrase;
seedSettingsViewModel.setPassphrase(null);
switch (type) { switch (type) {
case WalletType.monero: case WalletType.monero:
return monero!.createMoneroNewWalletCredentials( return monero!.createMoneroNewWalletCredentials(
name: name, language: options!.first as String, password: walletPassword, isPolyseed: options.last as bool); name: name,
language: options!.first as String,
password: walletPassword,
isPolyseed: options.last as bool);
case WalletType.bitcoin: case WalletType.bitcoin:
return bitcoin!.createBitcoinNewWalletCredentials(name: name, password: walletPassword); return bitcoin!.createBitcoinNewWalletCredentials(
name: name, password: walletPassword, passphrase: passphrase);
case WalletType.litecoin: case WalletType.litecoin:
return bitcoin!.createBitcoinNewWalletCredentials(name: name, password: walletPassword); return bitcoin!.createBitcoinNewWalletCredentials(
name: name, password: walletPassword, passphrase: passphrase);
case WalletType.haven: case WalletType.haven:
return haven!.createHavenNewWalletCredentials( return haven!.createHavenNewWalletCredentials(
name: name, language: options!.first as String, password: walletPassword); name: name, language: options!.first as String, password: walletPassword);
case WalletType.ethereum: case WalletType.ethereum:
return ethereum!.createEthereumNewWalletCredentials(name: name, password: walletPassword); return ethereum!.createEthereumNewWalletCredentials(name: name, password: walletPassword);
case WalletType.bitcoinCash: case WalletType.bitcoinCash:
return bitcoinCash!.createBitcoinCashNewWalletCredentials(name: name, password: walletPassword); return bitcoinCash!.createBitcoinCashNewWalletCredentials(
name: name, password: walletPassword, passphrase: passphrase);
case WalletType.nano: case WalletType.nano:
case WalletType.banano: case WalletType.banano:
return nano!.createNanoNewWalletCredentials(name: name); return nano!.createNanoNewWalletCredentials(name: name);

View file

@ -1,25 +1,26 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/core/generate_wallet_password.dart'; import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cake_wallet/di.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.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/store/app_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart';
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
import 'package:cake_wallet/wownero/wownero.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
part 'wallet_restore_view_model.g.dart'; part 'wallet_restore_view_model.g.dart';
@ -27,7 +28,7 @@ class WalletRestoreViewModel = WalletRestoreViewModelBase with _$WalletRestoreVi
abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService, WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
Box<WalletInfo> walletInfoSource, Box<WalletInfo> walletInfoSource, SeedSettingsViewModel seedSettingsViewModel,
{required WalletType type}) {required WalletType type})
: hasSeedLanguageSelector = : hasSeedLanguageSelector =
type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero, type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero,
@ -41,7 +42,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
type == WalletType.tron, type == WalletType.tron,
isButtonEnabled = false, isButtonEnabled = false,
mode = WalletRestoreMode.seed, mode = WalletRestoreMode.seed,
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) { super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
type: type, isRecovery: true) {
switch (type) { switch (type) {
case WalletType.monero: case WalletType.monero:
availableModes = WalletRestoreMode.values; availableModes = WalletRestoreMode.values;
@ -76,7 +78,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
final bool hasBlockchainHeightLanguageSelector; final bool hasBlockchainHeightLanguageSelector;
final bool hasRestoreFromPrivateKey; final bool hasRestoreFromPrivateKey;
bool get hasPassphrase => [WalletType.bitcoin, WalletType.litecoin].contains(type); bool get hasPassphrase =>
[WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(type);
@observable @observable
WalletRestoreMode mode; WalletRestoreMode mode;
@ -192,10 +195,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
case WalletType.nano: case WalletType.nano:
return nano!.createNanoRestoreWalletFromKeysCredentials( return nano!.createNanoRestoreWalletFromKeysCredentials(
name: name, name: name,
password: password, password: password,
seedKey: options['private_key'] as String, seedKey: options['private_key'] as String,
derivationType: options["derivationType"] as DerivationType); derivationType: derivationInfo!.derivationType!,
);
case WalletType.polygon: case WalletType.polygon:
return polygon!.createPolygonRestoreWalletFromPrivateKey( return polygon!.createPolygonRestoreWalletFromPrivateKey(
name: name, name: name,

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "هل أنت متأكد أنك تريد حذف محفظة ${wallet_name}؟", "delete_wallet_confirm_message": "هل أنت متأكد أنك تريد حذف محفظة ${wallet_name}؟",
"deleteConnectionConfirmationPrompt": "ـﺑ ﻝﺎﺼﺗﻻﺍ ﻑﺬﺣ ﺪﻳﺮﺗ ﻚﻧﺃ ﺪﻛﺄﺘﻣ ﺖﻧﺃ ﻞﻫ", "deleteConnectionConfirmationPrompt": "ـﺑ ﻝﺎﺼﺗﻻﺍ ﻑﺬﺣ ﺪﻳﺮﺗ ﻚﻧﺃ ﺪﻛﺄﺘﻣ ﺖﻧﺃ ﻞﻫ",
"denominations": "الطوائف", "denominations": "الطوائف",
"derivationpath": "مسار الاشتقاق",
"descending": "النزول", "descending": "النزول",
"description": "ﻒﺻﻭ", "description": "ﻒﺻﻭ",
"destination_tag": "علامة الوجهة:", "destination_tag": "علامة الوجهة:",
@ -207,6 +208,7 @@
"disable_buy": "تعطيل إجراء الشراء", "disable_buy": "تعطيل إجراء الشراء",
"disable_cake_2fa": "تعطيل 2 عامل المصادقة", "disable_cake_2fa": "تعطيل 2 عامل المصادقة",
"disable_exchange": "تعطيل التبادل", "disable_exchange": "تعطيل التبادل",
"disable_fee_api_warning": "من خلال إيقاف تشغيل هذا ، قد تكون معدلات الرسوم غير دقيقة في بعض الحالات ، لذلك قد ينتهي بك الأمر إلى دفع مبالغ زائدة أو دفع رسوم المعاملات الخاصة بك",
"disable_fiat": "تعطيل fiat", "disable_fiat": "تعطيل fiat",
"disable_sell": "قم بتعطيل إجراء البيع", "disable_sell": "قم بتعطيل إجراء البيع",
"disableBatteryOptimization": "تعطيل تحسين البطارية", "disableBatteryOptimization": "تعطيل تحسين البطارية",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "يرجى التأكد", "ledger_error_wrong_app": "يرجى التأكد",
"ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك", "ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك",
"light_theme": "فاتح", "light_theme": "فاتح",
"live_fee_rates": "أسعار الرسوم المباشرة عبر API",
"load_more": "تحميل المزيد", "load_more": "تحميل المزيد",
"loading_your_wallet": "يتم تحميل محفظتك", "loading_your_wallet": "يتم تحميل محفظتك",
"login": "تسجيل الدخول", "login": "تسجيل الدخول",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Сигурни ли сте, че искате да изтриете протфейла ${wallet_name}?", "delete_wallet_confirm_message": "Сигурни ли сте, че искате да изтриете протфейла ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Сигурни ли сте, че искате да изтриете връзката към", "deleteConnectionConfirmationPrompt": "Сигурни ли сте, че искате да изтриете връзката към",
"denominations": "Деноминации", "denominations": "Деноминации",
"derivationpath": "Пътят на производно",
"descending": "Низходящ", "descending": "Низходящ",
"description": "Описание", "description": "Описание",
"destination_tag": "Destination tag:", "destination_tag": "Destination tag:",
@ -207,6 +208,7 @@
"disable_buy": "Деактивирайте действието за покупка", "disable_buy": "Деактивирайте действието за покупка",
"disable_cake_2fa": "Деактивирайте Cake 2FA", "disable_cake_2fa": "Деактивирайте Cake 2FA",
"disable_exchange": "Деактивиране на борса", "disable_exchange": "Деактивиране на борса",
"disable_fee_api_warning": "Като изключите това, таксите могат да бъдат неточни в някои случаи, така че може да се препланите или да не плащате таксите за вашите транзакции",
"disable_fiat": "Деактивиране на fiat", "disable_fiat": "Деактивиране на fiat",
"disable_sell": "Деактивирайте действието за продажба", "disable_sell": "Деактивирайте действието за продажба",
"disableBatteryOptimization": "Деактивирайте оптимизацията на батерията", "disableBatteryOptimization": "Деактивирайте оптимизацията на батерията",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Моля, уверете се, че сте отворили правилното приложение на вашата книга", "ledger_error_wrong_app": "Моля, уверете се, че сте отворили правилното приложение на вашата книга",
"ledger_please_enable_bluetooth": "Моля, активирайте Bluetooth да открие вашата книга", "ledger_please_enable_bluetooth": "Моля, активирайте Bluetooth да открие вашата книга",
"light_theme": "Светло", "light_theme": "Светло",
"live_fee_rates": "Цени на таксите на живо чрез API",
"load_more": "Зареди още", "load_more": "Зареди още",
"loading_your_wallet": "Зареждане на портфейл", "loading_your_wallet": "Зареждане на портфейл",
"login": "Влизане", "login": "Влизане",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Opravdu chcete smazat ${wallet_name} peněženku?", "delete_wallet_confirm_message": "Opravdu chcete smazat ${wallet_name} peněženku?",
"deleteConnectionConfirmationPrompt": "Jste si jisti, že chcete smazat připojení k?", "deleteConnectionConfirmationPrompt": "Jste si jisti, že chcete smazat připojení k?",
"denominations": "Označení", "denominations": "Označení",
"derivationpath": "Derivační cesta",
"descending": "Klesající", "descending": "Klesající",
"description": "Popis", "description": "Popis",
"destination_tag": "Destination Tag:", "destination_tag": "Destination Tag:",
@ -207,6 +208,7 @@
"disable_buy": "Zakázat akci nákupu", "disable_buy": "Zakázat akci nákupu",
"disable_cake_2fa": "Zakázat Cake 2FA", "disable_cake_2fa": "Zakázat Cake 2FA",
"disable_exchange": "Zakázat směnárny", "disable_exchange": "Zakázat směnárny",
"disable_fee_api_warning": "Tímto vypnutím by sazby poplatků mohly být v některých případech nepřesné, takže byste mohli skončit přepláváním nebo nedoplatkem poplatků za vaše transakce",
"disable_fiat": "Zakázat fiat", "disable_fiat": "Zakázat fiat",
"disable_sell": "Zakázat akci prodeje", "disable_sell": "Zakázat akci prodeje",
"disableBatteryOptimization": "Zakázat optimalizaci baterie", "disableBatteryOptimization": "Zakázat optimalizaci baterie",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Ujistěte se, že se na své knize otevřete správnou aplikaci", "ledger_error_wrong_app": "Ujistěte se, že se na své knize otevřete správnou aplikaci",
"ledger_please_enable_bluetooth": "Umožněte prosím Bluetooth detekovat vaši knihu", "ledger_please_enable_bluetooth": "Umožněte prosím Bluetooth detekovat vaši knihu",
"light_theme": "Světlý", "light_theme": "Světlý",
"live_fee_rates": "Živé sazby poplatků prostřednictvím API",
"load_more": "Načíst další", "load_more": "Načíst další",
"loading_your_wallet": "Načítám peněženku", "loading_your_wallet": "Načítám peněženku",
"login": "Login", "login": "Login",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Sind Sie sicher, dass Sie das ${wallet_name} Wallet löschen möchten?", "delete_wallet_confirm_message": "Sind Sie sicher, dass Sie das ${wallet_name} Wallet löschen möchten?",
"deleteConnectionConfirmationPrompt": "Sind Sie sicher, dass Sie die Verbindung zu löschen möchten?", "deleteConnectionConfirmationPrompt": "Sind Sie sicher, dass Sie die Verbindung zu löschen möchten?",
"denominations": "Konfessionen", "denominations": "Konfessionen",
"derivationpath": "Ableitungspfad",
"descending": "Absteigend", "descending": "Absteigend",
"description": "Beschreibung", "description": "Beschreibung",
"destination_tag": "Ziel-Tag:", "destination_tag": "Ziel-Tag:",
@ -207,6 +208,7 @@
"disable_buy": "Kaufaktion deaktivieren", "disable_buy": "Kaufaktion deaktivieren",
"disable_cake_2fa": "Cake 2FA deaktivieren", "disable_cake_2fa": "Cake 2FA deaktivieren",
"disable_exchange": "Exchange deaktivieren", "disable_exchange": "Exchange deaktivieren",
"disable_fee_api_warning": "Wenn dies ausgeschaltet wird, sind die Gebührenquoten in einigen Fällen möglicherweise ungenau, sodass Sie die Gebühren für Ihre Transaktionen möglicherweise überbezahlt oder unterzahlt",
"disable_fiat": "Fiat deaktivieren", "disable_fiat": "Fiat deaktivieren",
"disable_sell": "Verkaufsaktion deaktivieren", "disable_sell": "Verkaufsaktion deaktivieren",
"disableBatteryOptimization": "Batterieoptimierung deaktivieren", "disableBatteryOptimization": "Batterieoptimierung deaktivieren",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Bitte stellen Sie sicher, dass Sie die richtige App auf Ihrem Ledger geöffnet haben", "ledger_error_wrong_app": "Bitte stellen Sie sicher, dass Sie die richtige App auf Ihrem Ledger geöffnet haben",
"ledger_please_enable_bluetooth": "Bitte aktivieren Sie Bluetooth um sich mit Ihren Ledger zu verbinden.", "ledger_please_enable_bluetooth": "Bitte aktivieren Sie Bluetooth um sich mit Ihren Ledger zu verbinden.",
"light_theme": "Hell", "light_theme": "Hell",
"live_fee_rates": "Live -Gebührenpreise über API",
"load_more": "Mehr laden", "load_more": "Mehr laden",
"loading_your_wallet": "Wallet wird geladen", "loading_your_wallet": "Wallet wird geladen",
"login": "Einloggen", "login": "Einloggen",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Are you sure that you want to delete ${wallet_name} wallet?", "delete_wallet_confirm_message": "Are you sure that you want to delete ${wallet_name} wallet?",
"deleteConnectionConfirmationPrompt": "Are you sure that you want to delete the connection to", "deleteConnectionConfirmationPrompt": "Are you sure that you want to delete the connection to",
"denominations": "Denominations", "denominations": "Denominations",
"derivationpath": "Derivation Path",
"descending": "Descending", "descending": "Descending",
"description": "Description", "description": "Description",
"destination_tag": "Destination tag:", "destination_tag": "Destination tag:",
@ -207,6 +208,7 @@
"disable_buy": "Disable buy action", "disable_buy": "Disable buy action",
"disable_cake_2fa": "Disable Cake 2FA", "disable_cake_2fa": "Disable Cake 2FA",
"disable_exchange": "Disable exchange", "disable_exchange": "Disable exchange",
"disable_fee_api_warning": "By turning this off, the fee rates might be inaccurate in some cases, so you might end up overpaying or underpaying the fees for your transactions",
"disable_fiat": "Disable fiat", "disable_fiat": "Disable fiat",
"disable_sell": "Disable sell action", "disable_sell": "Disable sell action",
"disableBatteryOptimization": "Disable Battery Optimization", "disableBatteryOptimization": "Disable Battery Optimization",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Please make sure you opend the right app on your ledger", "ledger_error_wrong_app": "Please make sure you opend the right app on your ledger",
"ledger_please_enable_bluetooth": "Please enable Bluetooth to detect your Ledger", "ledger_please_enable_bluetooth": "Please enable Bluetooth to detect your Ledger",
"light_theme": "Light", "light_theme": "Light",
"live_fee_rates": "Live fee rates via API",
"load_more": "Load more", "load_more": "Load more",
"loading_your_wallet": "Loading your wallet", "loading_your_wallet": "Loading your wallet",
"login": "Login", "login": "Login",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "¿Está seguro de que desea eliminar la billetera ${wallet_name}?", "delete_wallet_confirm_message": "¿Está seguro de que desea eliminar la billetera ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "¿Está seguro de que desea eliminar la conexión a", "deleteConnectionConfirmationPrompt": "¿Está seguro de que desea eliminar la conexión a",
"denominations": "Denominaciones", "denominations": "Denominaciones",
"derivationpath": "Ruta de derivación",
"descending": "Descendente", "descending": "Descendente",
"description": "Descripción", "description": "Descripción",
"destination_tag": "Etiqueta de destino:", "destination_tag": "Etiqueta de destino:",
@ -207,6 +208,7 @@
"disable_buy": "Desactivar acción de compra", "disable_buy": "Desactivar acción de compra",
"disable_cake_2fa": "Desactivar pastel 2FA", "disable_cake_2fa": "Desactivar pastel 2FA",
"disable_exchange": "Deshabilitar intercambio", "disable_exchange": "Deshabilitar intercambio",
"disable_fee_api_warning": "Al apagar esto, las tasas de tarifas pueden ser inexactas en algunos casos, por lo que puede terminar pagando en exceso o pagando menos las tarifas por sus transacciones",
"disable_fiat": "Deshabilitar fiat", "disable_fiat": "Deshabilitar fiat",
"disable_sell": "Desactivar acción de venta", "disable_sell": "Desactivar acción de venta",
"disableBatteryOptimization": "Deshabilitar la optimización de la batería", "disableBatteryOptimization": "Deshabilitar la optimización de la batería",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Por favor, asegúrese de abrir la aplicación correcta en su libro mayor.", "ledger_error_wrong_app": "Por favor, asegúrese de abrir la aplicación correcta en su libro mayor.",
"ledger_please_enable_bluetooth": "Habilite Bluetooth para detectar su libro mayor", "ledger_please_enable_bluetooth": "Habilite Bluetooth para detectar su libro mayor",
"light_theme": "Ligera", "light_theme": "Ligera",
"live_fee_rates": "Tasas de tarifas en vivo a través de API",
"load_more": "Carga más", "load_more": "Carga más",
"loading_your_wallet": "Cargando tu billetera", "loading_your_wallet": "Cargando tu billetera",
"login": "Iniciar sesión", "login": "Iniciar sesión",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Êtes-vous sûr de vouloir supprimer le portefeuille (wallet) ${wallet_name}?", "delete_wallet_confirm_message": "Êtes-vous sûr de vouloir supprimer le portefeuille (wallet) ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Êtes-vous sûr de vouloir supprimer la connexion à", "deleteConnectionConfirmationPrompt": "Êtes-vous sûr de vouloir supprimer la connexion à",
"denominations": "Dénominations", "denominations": "Dénominations",
"derivationpath": "Chemin de dérivation",
"descending": "Descendant", "descending": "Descendant",
"description": "Description", "description": "Description",
"destination_tag": "Tag de destination :", "destination_tag": "Tag de destination :",
@ -207,6 +208,7 @@
"disable_buy": "Désactiver l'action d'achat", "disable_buy": "Désactiver l'action d'achat",
"disable_cake_2fa": "Désactiver Cake 2FA", "disable_cake_2fa": "Désactiver Cake 2FA",
"disable_exchange": "Désactiver l'échange", "disable_exchange": "Désactiver l'échange",
"disable_fee_api_warning": "En désactivant cela, les taux de frais peuvent être inexacts dans certains cas, vous pourriez donc finir par payer trop ou sous-paiement les frais pour vos transactions",
"disable_fiat": "Désactiver les montants en fiat", "disable_fiat": "Désactiver les montants en fiat",
"disable_sell": "Désactiver l'action de vente", "disable_sell": "Désactiver l'action de vente",
"disableBatteryOptimization": "Désactiver l'optimisation de la batterie", "disableBatteryOptimization": "Désactiver l'optimisation de la batterie",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Veuillez vous assurer d'ouvrir la bonne application sur votre grand livre", "ledger_error_wrong_app": "Veuillez vous assurer d'ouvrir la bonne application sur votre grand livre",
"ledger_please_enable_bluetooth": "Veuillez activer Bluetooth pour détecter votre grand livre", "ledger_please_enable_bluetooth": "Veuillez activer Bluetooth pour détecter votre grand livre",
"light_theme": "Clair", "light_theme": "Clair",
"live_fee_rates": "Taux de frais en direct via l'API",
"load_more": "Charger plus", "load_more": "Charger plus",
"loading_your_wallet": "Chargement de votre portefeuille (wallet)", "loading_your_wallet": "Chargement de votre portefeuille (wallet)",
"login": "Utilisateur", "login": "Utilisateur",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Shin kun tabbata cewa kuna son share jakar ${wallet_name}?", "delete_wallet_confirm_message": "Shin kun tabbata cewa kuna son share jakar ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Shin kun tabbata cewa kuna son share haɗin zuwa", "deleteConnectionConfirmationPrompt": "Shin kun tabbata cewa kuna son share haɗin zuwa",
"denominations": "Denominations", "denominations": "Denominations",
"derivationpath": "Hanyar Nasara",
"descending": "Saukowa", "descending": "Saukowa",
"description": "Bayani", "description": "Bayani",
"destination_tag": "Tambarin makoma:", "destination_tag": "Tambarin makoma:",
@ -207,6 +208,7 @@
"disable_buy": "Kashe alama", "disable_buy": "Kashe alama",
"disable_cake_2fa": "Musaki Cake 2FA", "disable_cake_2fa": "Musaki Cake 2FA",
"disable_exchange": "Kashe musanya", "disable_exchange": "Kashe musanya",
"disable_fee_api_warning": "Ta hanyar juya wannan kashe, kudaden da zai iya zama ba daidai ba a wasu halaye, saboda haka zaku iya ƙare da overpaying ko a ƙarƙashin kudaden don ma'amaloli",
"disable_fiat": "Dakatar da fiat", "disable_fiat": "Dakatar da fiat",
"disable_sell": "Kashe karbuwa", "disable_sell": "Kashe karbuwa",
"disableBatteryOptimization": "Kashe ingantawa baturi", "disableBatteryOptimization": "Kashe ingantawa baturi",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Da fatan za a tabbata kun yi amfani da app ɗin dama akan dillalarku", "ledger_error_wrong_app": "Da fatan za a tabbata kun yi amfani da app ɗin dama akan dillalarku",
"ledger_please_enable_bluetooth": "Da fatan za a kunna Bluetooth don gano Ledger ɗinku", "ledger_please_enable_bluetooth": "Da fatan za a kunna Bluetooth don gano Ledger ɗinku",
"light_theme": "Haske", "light_theme": "Haske",
"live_fee_rates": "Kudin Kiɗa ta API",
"load_more": "Like more", "load_more": "Like more",
"loading_your_wallet": "Ana loda walat ɗin ku", "loading_your_wallet": "Ana loda walat ɗin ku",
"login": "Shiga", "login": "Shiga",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "क्या आप वाकई ${wallet_name} वॉलेट हटाना चाहते हैं?", "delete_wallet_confirm_message": "क्या आप वाकई ${wallet_name} वॉलेट हटाना चाहते हैं?",
"deleteConnectionConfirmationPrompt": "क्या आप वाकई कनेक्शन हटाना चाहते हैं?", "deleteConnectionConfirmationPrompt": "क्या आप वाकई कनेक्शन हटाना चाहते हैं?",
"denominations": "मूल्यवर्ग", "denominations": "मूल्यवर्ग",
"derivationpath": "व्युत्पत्ति पथ",
"descending": "अवरोही", "descending": "अवरोही",
"description": "विवरण", "description": "विवरण",
"destination_tag": "गंतव्य टैग:", "destination_tag": "गंतव्य टैग:",
@ -207,6 +208,7 @@
"disable_buy": "खरीद कार्रवाई अक्षम करें", "disable_buy": "खरीद कार्रवाई अक्षम करें",
"disable_cake_2fa": "केक 2FA अक्षम करें", "disable_cake_2fa": "केक 2FA अक्षम करें",
"disable_exchange": "एक्सचेंज अक्षम करें", "disable_exchange": "एक्सचेंज अक्षम करें",
"disable_fee_api_warning": "इसे बंद करने से, कुछ मामलों में शुल्क दरें गलत हो सकती हैं, इसलिए आप अपने लेनदेन के लिए फीस को कम कर सकते हैं या कम कर सकते हैं",
"disable_fiat": "िएट को अक्षम करें", "disable_fiat": "िएट को अक्षम करें",
"disable_sell": "बेचने की कार्रवाई अक्षम करें", "disable_sell": "बेचने की कार्रवाई अक्षम करें",
"disableBatteryOptimization": "बैटरी अनुकूलन अक्षम करें", "disableBatteryOptimization": "बैटरी अनुकूलन अक्षम करें",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "कृपया सुनिश्चित करें कि आप अपने लेजर पर सही ऐप को खोलते हैं", "ledger_error_wrong_app": "कृपया सुनिश्चित करें कि आप अपने लेजर पर सही ऐप को खोलते हैं",
"ledger_please_enable_bluetooth": "कृपया अपने बहीखाने का पता लगाने के लिए ब्लूटूथ को सक्षम करें", "ledger_please_enable_bluetooth": "कृपया अपने बहीखाने का पता लगाने के लिए ब्लूटूथ को सक्षम करें",
"light_theme": "रोशनी", "light_theme": "रोशनी",
"live_fee_rates": "एपीआई के माध्यम से लाइव शुल्क दरें",
"load_more": "और लोड करें", "load_more": "और लोड करें",
"loading_your_wallet": "अपना बटुआ लोड कर रहा है", "loading_your_wallet": "अपना बटुआ लोड कर रहा है",
"login": "लॉग इन करें", "login": "लॉग इन करें",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Jeste li sigurni da želite izbrisati ${wallet_name} novčanik?", "delete_wallet_confirm_message": "Jeste li sigurni da želite izbrisati ${wallet_name} novčanik?",
"deleteConnectionConfirmationPrompt": "Jeste li sigurni da želite izbrisati vezu s", "deleteConnectionConfirmationPrompt": "Jeste li sigurni da želite izbrisati vezu s",
"denominations": "Denominacije", "denominations": "Denominacije",
"derivationpath": "Put derivacije",
"descending": "Silazni", "descending": "Silazni",
"description": "Opis", "description": "Opis",
"destination_tag": "Odredišna oznaka:", "destination_tag": "Odredišna oznaka:",
@ -207,6 +208,7 @@
"disable_buy": "Onemogući kupnju", "disable_buy": "Onemogući kupnju",
"disable_cake_2fa": "Onemogući Cake 2FA", "disable_cake_2fa": "Onemogući Cake 2FA",
"disable_exchange": "Onemogući exchange", "disable_exchange": "Onemogući exchange",
"disable_fee_api_warning": "Isključivanjem ovoga, stope naknade u nekim bi slučajevima mogle biti netočne, tako da biste mogli preplatiti ili predati naknadu za vaše transakcije",
"disable_fiat": "Isključi, fiat", "disable_fiat": "Isključi, fiat",
"disable_sell": "Onemogući akciju prodaje", "disable_sell": "Onemogući akciju prodaje",
"disableBatteryOptimization": "Onemogući optimizaciju baterije", "disableBatteryOptimization": "Onemogući optimizaciju baterije",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Obavezno obavezno otvorite pravu aplikaciju na knjizi", "ledger_error_wrong_app": "Obavezno obavezno otvorite pravu aplikaciju na knjizi",
"ledger_please_enable_bluetooth": "Omogućite Bluetooth da otkrije svoju knjigu", "ledger_please_enable_bluetooth": "Omogućite Bluetooth da otkrije svoju knjigu",
"light_theme": "Svijetla", "light_theme": "Svijetla",
"live_fee_rates": "Stope naknada uživo putem API -ja",
"load_more": "Učitaj više", "load_more": "Učitaj više",
"loading_your_wallet": "Novčanik se učitava", "loading_your_wallet": "Novčanik se učitava",
"login": "Prijava", "login": "Prijava",

View file

@ -207,6 +207,7 @@
"disable_buy": "Անջատել գնում գործողությունը", "disable_buy": "Անջատել գնում գործողությունը",
"disable_cake_2fa": "Անջատել Cake 2FA", "disable_cake_2fa": "Անջատել Cake 2FA",
"disable_exchange": "Անջատել փոխանակումը", "disable_exchange": "Անջատել փոխանակումը",
"disable_fee_api_warning": "Դրանից անջատելով, վճարների տեմպերը որոշ դեպքերում կարող են անճիշտ լինել, այնպես որ դուք կարող եք վերջ տալ ձեր գործարքների համար վճարների գերավճարների կամ գերավճարների վրա",
"disable_fiat": "Անջատել ֆիատ", "disable_fiat": "Անջատել ֆիատ",
"disable_sell": "Անջատել վաճառք գործողությունը", "disable_sell": "Անջատել վաճառք գործողությունը",
"disableBatteryOptimization": "Անջատել մարտկոցի օպտիմիզացիան", "disableBatteryOptimization": "Անջատել մարտկոցի օպտիմիզացիան",
@ -354,6 +355,7 @@
"ledger_error_wrong_app": "Խնդրում ենք համոզվել, որ դուք բացել եք ճիշտ ծրագիրը ձեր Ledger-ում", "ledger_error_wrong_app": "Խնդրում ենք համոզվել, որ դուք բացել եք ճիշտ ծրագիրը ձեր Ledger-ում",
"ledger_please_enable_bluetooth": "Խնդրում ենք միացնել Bluetooth-ը ձեր Ledger-ը հայտնաբերելու համար", "ledger_please_enable_bluetooth": "Խնդրում ենք միացնել Bluetooth-ը ձեր Ledger-ը հայտնաբերելու համար",
"light_theme": "Լուսավոր", "light_theme": "Լուսավոր",
"live_fee_rates": "Ապակի վարձավճարներ API- ի միջոցով",
"load_more": "Բեռնել ավելին", "load_more": "Բեռնել ավելին",
"loading_your_wallet": "Ձեր հաշվեհամարը բեռնում է", "loading_your_wallet": "Ձեր հաշվեհամարը բեռնում է",
"login": "Մուտք", "login": "Մուտք",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Apakah Anda yakin ingin menghapus dompet ${wallet_name}?", "delete_wallet_confirm_message": "Apakah Anda yakin ingin menghapus dompet ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Apakah Anda yakin ingin menghapus koneksi ke", "deleteConnectionConfirmationPrompt": "Apakah Anda yakin ingin menghapus koneksi ke",
"denominations": "Denominasi", "denominations": "Denominasi",
"derivationpath": "Jalur derivasi",
"descending": "Menurun", "descending": "Menurun",
"description": "Keterangan", "description": "Keterangan",
"destination_tag": "Tag tujuan:", "destination_tag": "Tag tujuan:",
@ -207,6 +208,7 @@
"disable_buy": "Nonaktifkan tindakan beli", "disable_buy": "Nonaktifkan tindakan beli",
"disable_cake_2fa": "Nonaktifkan Kue 2FA", "disable_cake_2fa": "Nonaktifkan Kue 2FA",
"disable_exchange": "Nonaktifkan pertukaran", "disable_exchange": "Nonaktifkan pertukaran",
"disable_fee_api_warning": "Dengan mematikan ini, tarif biaya mungkin tidak akurat dalam beberapa kasus, jadi Anda mungkin akan membayar lebih atau membayar biaya untuk transaksi Anda",
"disable_fiat": "Nonaktifkan fiat", "disable_fiat": "Nonaktifkan fiat",
"disable_sell": "Nonaktifkan aksi jual", "disable_sell": "Nonaktifkan aksi jual",
"disableBatteryOptimization": "Nonaktifkan optimasi baterai", "disableBatteryOptimization": "Nonaktifkan optimasi baterai",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Pastikan Anda membuka aplikasi yang tepat di buku besar Anda", "ledger_error_wrong_app": "Pastikan Anda membuka aplikasi yang tepat di buku besar Anda",
"ledger_please_enable_bluetooth": "Harap aktifkan Bluetooth untuk mendeteksi buku besar Anda", "ledger_please_enable_bluetooth": "Harap aktifkan Bluetooth untuk mendeteksi buku besar Anda",
"light_theme": "Terang", "light_theme": "Terang",
"live_fee_rates": "Tarif biaya langsung melalui API",
"load_more": "Muat lebih banyak", "load_more": "Muat lebih banyak",
"loading_your_wallet": "Memuat dompet Anda", "loading_your_wallet": "Memuat dompet Anda",
"login": "Masuk", "login": "Masuk",

View file

@ -196,6 +196,7 @@
"delete_wallet_confirm_message": "Sei sicuro di voler eliminare il portafoglio ${wallet_name}?", "delete_wallet_confirm_message": "Sei sicuro di voler eliminare il portafoglio ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Sei sicuro di voler eliminare la connessione a", "deleteConnectionConfirmationPrompt": "Sei sicuro di voler eliminare la connessione a",
"denominations": "Denominazioni", "denominations": "Denominazioni",
"derivationpath": "Percorso di derivazione",
"descending": "Discendente", "descending": "Discendente",
"description": "Descrizione", "description": "Descrizione",
"destination_tag": "Tag destinazione:", "destination_tag": "Tag destinazione:",
@ -208,6 +209,7 @@
"disable_buy": "Disabilita l'azione di acquisto", "disable_buy": "Disabilita l'azione di acquisto",
"disable_cake_2fa": "Disabilita Cake 2FA", "disable_cake_2fa": "Disabilita Cake 2FA",
"disable_exchange": "Disabilita scambio", "disable_exchange": "Disabilita scambio",
"disable_fee_api_warning": "Disattivando questo, i tassi delle commissioni potrebbero essere inaccurati in alcuni casi, quindi potresti finire in eccesso o sostenere le commissioni per le transazioni",
"disable_fiat": "Disabilita fiat", "disable_fiat": "Disabilita fiat",
"disable_sell": "Disabilita l'azione di vendita", "disable_sell": "Disabilita l'azione di vendita",
"disableBatteryOptimization": "Disabilita l'ottimizzazione della batteria", "disableBatteryOptimization": "Disabilita l'ottimizzazione della batteria",
@ -355,6 +357,7 @@
"ledger_error_wrong_app": "Assicurati di aprire l'app giusta sul libro mastro", "ledger_error_wrong_app": "Assicurati di aprire l'app giusta sul libro mastro",
"ledger_please_enable_bluetooth": "Si prega di consentire al Bluetooth di rilevare il libro mastro", "ledger_please_enable_bluetooth": "Si prega di consentire al Bluetooth di rilevare il libro mastro",
"light_theme": "Bianco", "light_theme": "Bianco",
"live_fee_rates": "Tariffe delle commissioni dal vivo tramite API",
"load_more": "Carica di più", "load_more": "Carica di più",
"loading_your_wallet": "Caricamento portafoglio", "loading_your_wallet": "Caricamento portafoglio",
"login": "Accedi", "login": "Accedi",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "${wallet_name} ウォレットを削除してもよろしいですか?", "delete_wallet_confirm_message": "${wallet_name} ウォレットを削除してもよろしいですか?",
"deleteConnectionConfirmationPrompt": "への接続を削除してもよろしいですか?", "deleteConnectionConfirmationPrompt": "への接続を削除してもよろしいですか?",
"denominations": "宗派", "denominations": "宗派",
"derivationpath": "派生パス",
"descending": "下降", "descending": "下降",
"description": "説明", "description": "説明",
"destination_tag": "宛先タグ:", "destination_tag": "宛先タグ:",
@ -207,6 +208,7 @@
"disable_buy": "購入アクションを無効にする", "disable_buy": "購入アクションを無効にする",
"disable_cake_2fa": "Cake 2FA を無効にする", "disable_cake_2fa": "Cake 2FA を無効にする",
"disable_exchange": "交換を無効にする", "disable_exchange": "交換を無効にする",
"disable_fee_api_warning": "これをオフにすることで、料金金利は場合によっては不正確になる可能性があるため、取引の費用が過払いまたは不足している可能性があります",
"disable_fiat": "フィアットを無効にする", "disable_fiat": "フィアットを無効にする",
"disable_sell": "販売アクションを無効にする", "disable_sell": "販売アクションを無効にする",
"disableBatteryOptimization": "バッテリーの最適化を無効にします", "disableBatteryOptimization": "バッテリーの最適化を無効にします",
@ -355,6 +357,7 @@
"ledger_error_wrong_app": "元帳に適切なアプリを開始するようにしてください", "ledger_error_wrong_app": "元帳に適切なアプリを開始するようにしてください",
"ledger_please_enable_bluetooth": "Bluetoothが元帳を検出できるようにしてください", "ledger_please_enable_bluetooth": "Bluetoothが元帳を検出できるようにしてください",
"light_theme": "光", "light_theme": "光",
"live_fee_rates": "API経由のライブ料金",
"load_more": "もっと読み込む", "load_more": "もっと読み込む",
"loading_your_wallet": "ウォレットをロードしています", "loading_your_wallet": "ウォレットをロードしています",
"login": "ログイン", "login": "ログイン",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "${wallet_name} 지갑을 삭제하시겠습니까?", "delete_wallet_confirm_message": "${wallet_name} 지갑을 삭제하시겠습니까?",
"deleteConnectionConfirmationPrompt": "다음 연결을 삭제하시겠습니까?", "deleteConnectionConfirmationPrompt": "다음 연결을 삭제하시겠습니까?",
"denominations": "교파", "denominations": "교파",
"derivationpath": "파생 경로",
"descending": "내림차순", "descending": "내림차순",
"description": "설명", "description": "설명",
"destination_tag": "목적지 태그:", "destination_tag": "목적지 태그:",
@ -207,6 +208,7 @@
"disable_buy": "구매 행동 비활성화", "disable_buy": "구매 행동 비활성화",
"disable_cake_2fa": "케이크 2FA 비활성화", "disable_cake_2fa": "케이크 2FA 비활성화",
"disable_exchange": "교환 비활성화", "disable_exchange": "교환 비활성화",
"disable_fee_api_warning": "이것을 끄면 경우에 따라 수수료가 부정확 할 수 있으므로 거래 수수료를 초과 지불하거나 지불 할 수 있습니다.",
"disable_fiat": "법정화폐 비활성화", "disable_fiat": "법정화폐 비활성화",
"disable_sell": "판매 조치 비활성화", "disable_sell": "판매 조치 비활성화",
"disableBatteryOptimization": "배터리 최적화를 비활성화합니다", "disableBatteryOptimization": "배터리 최적화를 비활성화합니다",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "원장에서 올바른 앱을 반대하는지 확인하십시오.", "ledger_error_wrong_app": "원장에서 올바른 앱을 반대하는지 확인하십시오.",
"ledger_please_enable_bluetooth": "Bluetooth가 원장을 감지 할 수 있도록하십시오", "ledger_please_enable_bluetooth": "Bluetooth가 원장을 감지 할 수 있도록하십시오",
"light_theme": "빛", "light_theme": "빛",
"live_fee_rates": "API를 통한 라이브 요금 요금",
"load_more": "더로드하십시오", "load_more": "더로드하십시오",
"loading_your_wallet": "지갑 넣기", "loading_your_wallet": "지갑 넣기",
"login": "로그인", "login": "로그인",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "${wallet_name} ပိုက်ဆံအိတ်ကို ဖျက်လိုသည်မှာ သေချာပါသလား။", "delete_wallet_confirm_message": "${wallet_name} ပိုက်ဆံအိတ်ကို ဖျက်လိုသည်မှာ သေချာပါသလား။",
"deleteConnectionConfirmationPrompt": "ချိတ်ဆက်မှုကို ဖျက်လိုသည်မှာ သေချာပါသလား။", "deleteConnectionConfirmationPrompt": "ချိတ်ဆက်မှုကို ဖျက်လိုသည်မှာ သေချာပါသလား။",
"denominations": "ဂိုဏ်းချုပ်ပစ္စည်းများ", "denominations": "ဂိုဏ်းချုပ်ပစ္စည်းများ",
"derivationpath": "derivation လမ်းကြောင်း",
"descending": "ဆင်း", "descending": "ဆင်း",
"description": "ဖော်ပြချက်", "description": "ဖော်ပြချက်",
"destination_tag": "ခရီးဆုံးအမှတ်-", "destination_tag": "ခရီးဆုံးအမှတ်-",
@ -207,6 +208,7 @@
"disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။", "disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
"disable_cake_2fa": "ကိတ်မုန့် 2FA ကို ပိတ်ပါ။", "disable_cake_2fa": "ကိတ်မုန့် 2FA ကို ပိတ်ပါ။",
"disable_exchange": "လဲလှယ်မှုကို ပိတ်ပါ။", "disable_exchange": "လဲလှယ်မှုကို ပိတ်ပါ။",
"disable_fee_api_warning": "ဤအရာကိုဖွင့်ခြင်းအားဖြင့်အချို့သောကိစ္စရပ်များတွင်အခကြေးငွေနှုန်းထားများသည်တိကျမှုရှိနိုင်သည်,",
"disable_fiat": "Fiat ကိုပိတ်ပါ။", "disable_fiat": "Fiat ကိုပိတ်ပါ။",
"disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။", "disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
"disableBatteryOptimization": "ဘက်ထရီ optimization ကိုပိတ်ပါ", "disableBatteryOptimization": "ဘက်ထရီ optimization ကိုပိတ်ပါ",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "ကျေးဇူးပြု. သင့်လက်ျာအက်ပ်ကိုသင်၏ Ledger တွင်ဖွင့်ရန်သေချာစေပါ", "ledger_error_wrong_app": "ကျေးဇူးပြု. သင့်လက်ျာအက်ပ်ကိုသင်၏ Ledger တွင်ဖွင့်ရန်သေချာစေပါ",
"ledger_please_enable_bluetooth": "သင်၏ Ledger ကိုရှာဖွေရန် Bluetooth ကိုဖွင့်ပါ", "ledger_please_enable_bluetooth": "သင်၏ Ledger ကိုရှာဖွေရန် Bluetooth ကိုဖွင့်ပါ",
"light_theme": "အလင်း", "light_theme": "အလင်း",
"live_fee_rates": "API မှတစ်ဆင့် Live အခကြေးငွေနှုန်းထားများ",
"load_more": "ပိုပြီး load", "load_more": "ပိုပြီး load",
"loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။", "loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။",
"login": "လော့ဂ်အင်", "login": "လော့ဂ်အင်",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Weet u zeker dat u de portemonnee van ${wallet_name} wilt verwijderen?", "delete_wallet_confirm_message": "Weet u zeker dat u de portemonnee van ${wallet_name} wilt verwijderen?",
"deleteConnectionConfirmationPrompt": "Weet u zeker dat u de verbinding met", "deleteConnectionConfirmationPrompt": "Weet u zeker dat u de verbinding met",
"denominations": "Denominaties", "denominations": "Denominaties",
"derivationpath": "Afleidingspad",
"descending": "Aflopend", "descending": "Aflopend",
"description": "Beschrijving", "description": "Beschrijving",
"destination_tag": "Bestemmingstag:", "destination_tag": "Bestemmingstag:",
@ -207,6 +208,7 @@
"disable_buy": "Koopactie uitschakelen", "disable_buy": "Koopactie uitschakelen",
"disable_cake_2fa": "Taart 2FA uitschakelen", "disable_cake_2fa": "Taart 2FA uitschakelen",
"disable_exchange": "Uitwisseling uitschakelen", "disable_exchange": "Uitwisseling uitschakelen",
"disable_fee_api_warning": "Door dit uit te schakelen, kunnen de tarieven in sommige gevallen onnauwkeurig zijn, dus u kunt de vergoedingen voor uw transacties te veel betalen of te weinig betalen",
"disable_fiat": "Schakel Fiat uit", "disable_fiat": "Schakel Fiat uit",
"disable_sell": "Verkoopactie uitschakelen", "disable_sell": "Verkoopactie uitschakelen",
"disableBatteryOptimization": "Schakel de batterijoptimalisatie uit", "disableBatteryOptimization": "Schakel de batterijoptimalisatie uit",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Zorg ervoor dat u de juiste app op uw grootboek opent", "ledger_error_wrong_app": "Zorg ervoor dat u de juiste app op uw grootboek opent",
"ledger_please_enable_bluetooth": "Schakel Bluetooth in staat om uw grootboek te detecteren", "ledger_please_enable_bluetooth": "Schakel Bluetooth in staat om uw grootboek te detecteren",
"light_theme": "Licht", "light_theme": "Licht",
"live_fee_rates": "Live -tarieven via API",
"load_more": "Meer laden", "load_more": "Meer laden",
"loading_your_wallet": "Uw portemonnee laden", "loading_your_wallet": "Uw portemonnee laden",
"login": "Log in", "login": "Log in",

View file

@ -195,6 +195,7 @@
"delete_wallet_confirm_message": "Czy na pewno chcesz usunąć portfel ${wallet_name}?", "delete_wallet_confirm_message": "Czy na pewno chcesz usunąć portfel ${wallet_name}?",
"deleteConnectionConfirmationPrompt": "Czy na pewno chcesz usunąć połączenie z", "deleteConnectionConfirmationPrompt": "Czy na pewno chcesz usunąć połączenie z",
"denominations": "Wyznaczenia", "denominations": "Wyznaczenia",
"derivationpath": "Ścieżka pochodna",
"descending": "Schodzenie", "descending": "Schodzenie",
"description": "Opis", "description": "Opis",
"destination_tag": "Tag docelowy:", "destination_tag": "Tag docelowy:",
@ -207,6 +208,7 @@
"disable_buy": "Wyłącz akcję kupna", "disable_buy": "Wyłącz akcję kupna",
"disable_cake_2fa": "Wyłącz Cake 2FA", "disable_cake_2fa": "Wyłącz Cake 2FA",
"disable_exchange": "Wyłącz wymianę", "disable_exchange": "Wyłącz wymianę",
"disable_fee_api_warning": "Wyłączając to, stawki opłaty mogą być w niektórych przypadkach niedokładne, więc możesz skończyć się przepłaceniem lub wynagrodzeniem opłat za transakcje",
"disable_fiat": "Wyłącz waluty FIAT", "disable_fiat": "Wyłącz waluty FIAT",
"disable_sell": "Wyłącz akcję sprzedaży", "disable_sell": "Wyłącz akcję sprzedaży",
"disableBatteryOptimization": "Wyłącz optymalizację baterii", "disableBatteryOptimization": "Wyłącz optymalizację baterii",
@ -354,6 +356,7 @@
"ledger_error_wrong_app": "Upewnij się, że opisz odpowiednią aplikację na swojej księdze", "ledger_error_wrong_app": "Upewnij się, że opisz odpowiednią aplikację na swojej księdze",
"ledger_please_enable_bluetooth": "Włącz Bluetooth wykrywanie księgi", "ledger_please_enable_bluetooth": "Włącz Bluetooth wykrywanie księgi",
"light_theme": "Jasny", "light_theme": "Jasny",
"live_fee_rates": "Stawki opłaty na żywo za pośrednictwem API",
"load_more": "Załaduj więcej", "load_more": "Załaduj więcej",
"loading_your_wallet": "Ładowanie portfela", "loading_your_wallet": "Ładowanie portfela",
"login": "Login", "login": "Login",

Some files were not shown because too many files have changed in this diff Show more