mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-12 09:32:33 +00:00
Merge branch 'mweb' of https://github.com/cake-tech/cake_wallet into mweb-bg-sync-2
This commit is contained in:
commit
b9961cfce3
124 changed files with 3143 additions and 998 deletions
BIN
assets/images/wallet_group.png
Normal file
BIN
assets/images/wallet_group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
|
@ -1,14 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin;
|
||||
|
||||
List<int> addressToOutputScript(String address, bitcoin.BasedUtxoNetwork network) {
|
||||
try {
|
||||
if (network == bitcoin.BitcoinCashNetwork.mainnet) {
|
||||
return bitcoin.BitcoinCashAddress(address).baseAddress.toScriptPubKey().toBytes();
|
||||
}
|
||||
return bitcoin.addressToOutputScript(address: address, network: network);
|
||||
} catch (err) {
|
||||
print(err);
|
||||
return Uint8List(0);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cw_bitcoin/script_hash.dart' as sh;
|
||||
|
||||
abstract class BaseBitcoinAddressRecord {
|
||||
BaseBitcoinAddressRecord(
|
||||
|
@ -65,8 +64,8 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
required super.type,
|
||||
String? scriptHash,
|
||||
required super.network,
|
||||
}) : scriptHash =
|
||||
scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null);
|
||||
}) : scriptHash = scriptHash ??
|
||||
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network) : null);
|
||||
|
||||
factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
@ -92,7 +91,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
|
||||
String getScriptHash(BasedUtxoNetwork network) {
|
||||
if (scriptHash != null) return scriptHash!;
|
||||
scriptHash = sh.scriptHash(address, network: network);
|
||||
scriptHash = BitcoinAddressUtils.scriptHash(address, network: network);
|
||||
return scriptHash!;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,17 @@ class BitcoinNewWalletCredentials extends WalletCredentials {
|
|||
DerivationType? derivationType,
|
||||
String? derivationPath,
|
||||
String? passphrase,
|
||||
this.mnemonic,
|
||||
String? parentAddress,
|
||||
}) : super(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
final String? mnemonic;
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
|
|
@ -41,7 +41,7 @@ class BitcoinWalletService extends WalletService<
|
|||
case DerivationType.bip39:
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
mnemonic = await MnemonicBip39.generate(strength: strength);
|
||||
mnemonic = credentials.mnemonic ?? await MnemonicBip39.generate(strength: strength);
|
||||
break;
|
||||
case DerivationType.electrum:
|
||||
default:
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'dart:io';
|
|||
import 'dart:typed_data';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_bitcoin/script_hash.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
|
@ -48,6 +47,7 @@ class ElectrumClient {
|
|||
final Map<String, SocketTask> _tasks;
|
||||
Map<String, SocketTask> get tasks => _tasks;
|
||||
final Map<String, String> _errors;
|
||||
ConnectionStatus _connectionStatus = ConnectionStatus.disconnected;
|
||||
bool _isConnected;
|
||||
Timer? _aliveTimer;
|
||||
String unterminatedString;
|
||||
|
@ -57,11 +57,13 @@ class ElectrumClient {
|
|||
|
||||
Future<void> connectToUri(Uri uri, {bool? useSSL}) async {
|
||||
this.uri = uri;
|
||||
this.useSSL = useSSL;
|
||||
await connect(host: uri.host, port: uri.port, useSSL: useSSL);
|
||||
if (useSSL != null) {
|
||||
this.useSSL = useSSL;
|
||||
}
|
||||
await connect(host: uri.host, port: uri.port);
|
||||
}
|
||||
|
||||
Future<void> connect({required String host, required int port, bool? useSSL}) async {
|
||||
Future<void> connect({required String host, required int port}) async {
|
||||
_setConnectionStatus(ConnectionStatus.connecting);
|
||||
|
||||
try {
|
||||
|
@ -80,15 +82,26 @@ class ElectrumClient {
|
|||
onBadCertificate: (_) => true,
|
||||
);
|
||||
}
|
||||
} catch (_) {
|
||||
_setConnectionStatus(ConnectionStatus.failed);
|
||||
} catch (e) {
|
||||
if (e is HandshakeException) {
|
||||
useSSL = !(useSSL ?? false);
|
||||
}
|
||||
|
||||
if (_connectionStatus != ConnectionStatus.connecting) {
|
||||
_setConnectionStatus(ConnectionStatus.failed);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (socket == null) {
|
||||
_setConnectionStatus(ConnectionStatus.failed);
|
||||
if (_connectionStatus != ConnectionStatus.connecting) {
|
||||
_setConnectionStatus(ConnectionStatus.failed);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_setConnectionStatus(ConnectionStatus.connected);
|
||||
|
||||
socket!.listen(
|
||||
|
@ -118,7 +131,7 @@ class ElectrumClient {
|
|||
socket?.destroy();
|
||||
_setConnectionStatus(ConnectionStatus.disconnected);
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
},
|
||||
|
@ -217,25 +230,6 @@ class ElectrumClient {
|
|||
return [];
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
||||
String address, BasedUtxoNetwork network) =>
|
||||
call(
|
||||
method: 'blockchain.scripthash.listunspent',
|
||||
params: [scriptHash(address, network: network)]).then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, dynamic>) {
|
||||
val['address'] = address;
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, dynamic>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.listunspent', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
|
@ -272,16 +266,12 @@ class ElectrumClient {
|
|||
try {
|
||||
final result = await callWithTimeout(
|
||||
method: 'blockchain.transaction.get', params: [hash, verbose], timeout: 10000);
|
||||
if (result is Map<String, dynamic>) {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
} on RequestFailedTimeoutException catch (_) {
|
||||
return <String, dynamic>{};
|
||||
} catch (e) {
|
||||
print("getTransaction: ${e.toString()}");
|
||||
return <String, dynamic>{};
|
||||
}
|
||||
return <String, dynamic>{};
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getTransactionVerbose({required String hash}) =>
|
||||
|
@ -326,9 +316,8 @@ class ElectrumClient {
|
|||
await call(method: 'blockchain.block.get_header', params: [height]) as Map<String, dynamic>;
|
||||
|
||||
BehaviorSubject<Object>? tweaksSubscribe({required int height, required int count}) {
|
||||
_id += 1;
|
||||
return subscribe<Object>(
|
||||
id: 'blockchain.tweaks.subscribe:${height + count}',
|
||||
id: 'blockchain.tweaks.subscribe',
|
||||
method: 'blockchain.tweaks.subscribe',
|
||||
params: [height, count, false],
|
||||
);
|
||||
|
@ -545,6 +534,7 @@ class ElectrumClient {
|
|||
|
||||
void _setConnectionStatus(ConnectionStatus status) {
|
||||
onConnectionStatusChange?.call(status);
|
||||
_connectionStatus = status;
|
||||
_isConnected = status == ConnectionStatus.connected;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,20 +23,25 @@ class ElectrumTransactionBundle {
|
|||
|
||||
class ElectrumTransactionInfo extends TransactionInfo {
|
||||
List<BitcoinSilentPaymentsUnspent>? unspents;
|
||||
bool isReceivedSilentPayment;
|
||||
|
||||
ElectrumTransactionInfo(this.type,
|
||||
{required String id,
|
||||
int? height,
|
||||
required int amount,
|
||||
int? fee,
|
||||
List<String>? inputAddresses,
|
||||
List<String>? outputAddresses,
|
||||
required TransactionDirection direction,
|
||||
required bool isPending,
|
||||
required DateTime date,
|
||||
required int confirmations,
|
||||
String? to,
|
||||
this.unspents}) {
|
||||
ElectrumTransactionInfo(
|
||||
this.type, {
|
||||
required String id,
|
||||
int? height,
|
||||
required int amount,
|
||||
int? fee,
|
||||
List<String>? inputAddresses,
|
||||
List<String>? outputAddresses,
|
||||
required TransactionDirection direction,
|
||||
required bool isPending,
|
||||
required bool isReplaced,
|
||||
required DateTime date,
|
||||
required int confirmations,
|
||||
String? to,
|
||||
this.unspents,
|
||||
this.isReceivedSilentPayment = false,
|
||||
}) {
|
||||
this.id = id;
|
||||
this.height = height;
|
||||
this.amount = amount;
|
||||
|
@ -46,6 +51,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
this.direction = direction;
|
||||
this.date = date;
|
||||
this.isPending = isPending;
|
||||
this.isReplaced = isReplaced;
|
||||
this.confirmations = confirmations;
|
||||
this.to = to;
|
||||
}
|
||||
|
@ -94,6 +100,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
id: id,
|
||||
height: height,
|
||||
isPending: false,
|
||||
isReplaced: false,
|
||||
fee: fee,
|
||||
direction: direction,
|
||||
amount: amount,
|
||||
|
@ -169,6 +176,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
id: bundle.originalTransaction.txId(),
|
||||
height: height,
|
||||
isPending: bundle.confirmations == 0,
|
||||
isReplaced: false,
|
||||
inputAddresses: inputAddresses,
|
||||
outputAddresses: outputAddresses,
|
||||
fee: fee,
|
||||
|
@ -192,6 +200,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool,
|
||||
isReplaced: data['isReplaced'] as bool? ?? false,
|
||||
confirmations: data['confirmations'] as int,
|
||||
inputAddresses:
|
||||
inputAddresses.isEmpty ? [] : inputAddresses.map((e) => e.toString()).toList(),
|
||||
|
@ -202,6 +211,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
.map((unspent) =>
|
||||
BitcoinSilentPaymentsUnspent.fromJSON(null, unspent as Map<String, dynamic>))
|
||||
.toList(),
|
||||
isReceivedSilentPayment: data['isReceivedSilentPayment'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -233,6 +243,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
direction: direction,
|
||||
date: date,
|
||||
isPending: isPending,
|
||||
isReplaced: isReplaced ?? false,
|
||||
inputAddresses: inputAddresses,
|
||||
outputAddresses: outputAddresses,
|
||||
confirmations: info.confirmations);
|
||||
|
@ -246,16 +257,18 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
m['direction'] = direction.index;
|
||||
m['date'] = date.millisecondsSinceEpoch;
|
||||
m['isPending'] = isPending;
|
||||
m['isReplaced'] = isReplaced;
|
||||
m['confirmations'] = confirmations;
|
||||
m['fee'] = fee;
|
||||
m['to'] = to;
|
||||
m['unspents'] = unspents?.map((e) => e.toJson()).toList() ?? [];
|
||||
m['inputAddresses'] = inputAddresses;
|
||||
m['outputAddresses'] = outputAddresses;
|
||||
m['isReceivedSilentPayment'] = isReceivedSilentPayment;
|
||||
return m;
|
||||
}
|
||||
|
||||
String toString() {
|
||||
return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, confirmations: $confirmations, to: $to, unspent: $unspents, inputAddresses: $inputAddresses, outputAddresses: $outputAddresses)';
|
||||
return 'ElectrumTransactionInfo(id: $id, height: $height, amount: $amount, fee: $fee, direction: $direction, date: $date, isPending: $isPending, isReplaced: $isReplaced, confirmations: $confirmations, to: $to, unspent: $unspents, inputAddresses: $inputAddresses, outputAddresses: $outputAddresses)';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
|||
import 'package:cw_bitcoin/exceptions.dart';
|
||||
import 'package:cw_bitcoin/litecoin_wallet.dart';
|
||||
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cw_bitcoin/script_hash.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
|
@ -52,8 +51,6 @@ part 'electrum_wallet.g.dart';
|
|||
|
||||
class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet;
|
||||
|
||||
const int TWEAKS_COUNT = 25;
|
||||
|
||||
abstract class ElectrumWalletBase
|
||||
extends WalletBase<ElectrumBalance, ElectrumTransactionHistory, ElectrumTransactionInfo>
|
||||
with Store, WalletKeysFile {
|
||||
|
@ -173,14 +170,14 @@ abstract class ElectrumWalletBase
|
|||
Set<String> get addressesSet => walletAddresses.allAddresses.map((addr) => addr.address).toSet();
|
||||
|
||||
List<String> get scriptHashes => walletAddresses.addressesByReceiveType
|
||||
.where((addr) => addressTypeFromStr(addr.address, network) is! MwebAddress)
|
||||
.map((addr) => scriptHash(addr.address, network: network))
|
||||
.where((addr) => RegexUtils.addressTypeFromStr(addr.address, network) is! MwebAddress)
|
||||
.map((addr) => (addr as BitcoinAddressRecord).getScriptHash(network))
|
||||
.toList();
|
||||
|
||||
List<String> get publicScriptHashes => walletAddresses.allAddresses
|
||||
.where((addr) => !addr.isHidden)
|
||||
.where((addr) => addressTypeFromStr(addr.address, network) is! MwebAddress)
|
||||
.map((addr) => scriptHash(addr.address, network: network))
|
||||
.where((addr) => RegexUtils.addressTypeFromStr(addr.address, network) is! MwebAddress)
|
||||
.map((addr) => addr.getScriptHash(network))
|
||||
.toList();
|
||||
|
||||
String get xpub => accountHD.publicKey.toExtended;
|
||||
|
@ -221,7 +218,7 @@ abstract class ElectrumWalletBase
|
|||
silentPaymentsScanningActive = active;
|
||||
|
||||
if (active) {
|
||||
syncStatus = StartingScanSyncStatus();
|
||||
syncStatus = AttemptingScanSyncStatus();
|
||||
|
||||
final tip = await getUpdatedChainTip();
|
||||
|
||||
|
@ -300,12 +297,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> _setListeners(
|
||||
int height, {
|
||||
int? chainTipParam,
|
||||
bool? doSingleScan,
|
||||
bool? usingSupportedNode,
|
||||
}) async {
|
||||
Future<void> _setListeners(int height, {int? chainTipParam, bool? doSingleScan}) async {
|
||||
final chainTip = chainTipParam ?? await getUpdatedChainTip();
|
||||
|
||||
if (chainTip == height) {
|
||||
|
@ -313,7 +305,7 @@ abstract class ElectrumWalletBase
|
|||
return;
|
||||
}
|
||||
|
||||
syncStatus = StartingScanSyncStatus();
|
||||
syncStatus = AttemptingScanSyncStatus();
|
||||
|
||||
if (_isolate != null) {
|
||||
final runningIsolate = await _isolate!;
|
||||
|
@ -576,7 +568,7 @@ abstract class ElectrumWalletBase
|
|||
electrumClient.onConnectionStatusChange = _onConnectionStatusChange;
|
||||
|
||||
await electrumClient.connectToUri(node.uri, useSSL: node.useSSL);
|
||||
|
||||
|
||||
int secondsWaited = 0;
|
||||
while (!electrumClient.isConnected) {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
|
@ -585,7 +577,9 @@ abstract class ElectrumWalletBase
|
|||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (e, stacktrace) {
|
||||
print(stacktrace);
|
||||
print(e.toString());
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
@ -626,7 +620,7 @@ abstract class ElectrumWalletBase
|
|||
allInputsAmount += utx.value;
|
||||
leftAmount = leftAmount - utx.value;
|
||||
|
||||
final address = addressTypeFromStr(utx.address, network);
|
||||
final address = RegexUtils.addressTypeFromStr(utx.address, network);
|
||||
ECPrivate? privkey;
|
||||
bool? isSilentPayment = false;
|
||||
|
||||
|
@ -822,10 +816,11 @@ abstract class ElectrumWalletBase
|
|||
outputs: outputs,
|
||||
utxoDetails: utxoDetails,
|
||||
);
|
||||
final address = addressTypeFromStr(changeAddress, network);
|
||||
final address = RegexUtils.addressTypeFromStr(changeAddress, network);
|
||||
outputs.add(BitcoinOutput(
|
||||
address: address,
|
||||
value: BigInt.from(amountLeftForChangeAndFee),
|
||||
isChange: true,
|
||||
));
|
||||
|
||||
int fee = await calcFee(
|
||||
|
@ -848,8 +843,12 @@ abstract class ElectrumWalletBase
|
|||
|
||||
if (!_isBelowDust(amountLeftForChange)) {
|
||||
// Here, lastOutput already is change, return the amount left without the fee to the user's address.
|
||||
outputs[outputs.length - 1] =
|
||||
BitcoinOutput(address: lastOutput.address, value: BigInt.from(amountLeftForChange));
|
||||
outputs[outputs.length - 1] = BitcoinOutput(
|
||||
address: lastOutput.address,
|
||||
value: BigInt.from(amountLeftForChange),
|
||||
isSilentPayment: lastOutput.isSilentPayment,
|
||||
isChange: true,
|
||||
);
|
||||
} else {
|
||||
// If has change that is lower than dust, will end up with tx rejected by network rules, so estimate again without the added change
|
||||
outputs.removeLast();
|
||||
|
@ -984,18 +983,27 @@ abstract class ElectrumWalletBase
|
|||
|
||||
credentialsAmount += outputAmount;
|
||||
|
||||
final address =
|
||||
addressTypeFromStr(out.isParsedAddress ? out.extractedAddress! : out.address, network);
|
||||
final address = RegexUtils.addressTypeFromStr(
|
||||
out.isParsedAddress ? out.extractedAddress! : out.address, network);
|
||||
final isSilentPayment = address is SilentPaymentAddress;
|
||||
|
||||
if (address is SilentPaymentAddress) {
|
||||
if (isSilentPayment) {
|
||||
hasSilentPayment = true;
|
||||
}
|
||||
|
||||
if (sendAll) {
|
||||
// The value will be changed after estimating the Tx size and deducting the fee from the total to be sent
|
||||
outputs.add(BitcoinOutput(address: address, value: BigInt.from(0)));
|
||||
outputs.add(BitcoinOutput(
|
||||
address: address,
|
||||
value: BigInt.from(0),
|
||||
isSilentPayment: isSilentPayment,
|
||||
));
|
||||
} else {
|
||||
outputs.add(BitcoinOutput(address: address, value: BigInt.from(outputAmount)));
|
||||
outputs.add(BitcoinOutput(
|
||||
address: address,
|
||||
value: BigInt.from(outputAmount),
|
||||
isSilentPayment: isSilentPayment,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1285,12 +1293,7 @@ abstract class ElectrumWalletBase
|
|||
|
||||
@action
|
||||
@override
|
||||
Future<void> rescan({
|
||||
required int height,
|
||||
int? chainTip,
|
||||
ScanData? scanData,
|
||||
bool? doSingleScan,
|
||||
}) async {
|
||||
Future<void> rescan({required int height, bool? doSingleScan}) async {
|
||||
silentPaymentsScanningActive = true;
|
||||
_setListeners(height, doSingleScan: doSingleScan);
|
||||
}
|
||||
|
@ -1436,14 +1439,16 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
||||
int transactionVSize(String transactionHex) => BtcTransaction.fromRaw(transactionHex).getVSize();
|
||||
|
||||
Future<String?> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
||||
try {
|
||||
final bundle = await getTransactionExpanded(hash: tx.txHash);
|
||||
_updateInputsAndOutputs(tx, bundle);
|
||||
if (bundle.confirmations > 0) return false;
|
||||
return bundle.originalTransaction.canReplaceByFee;
|
||||
if (bundle.confirmations > 0) return null;
|
||||
return bundle.originalTransaction.canReplaceByFee ? bundle.originalTransaction.toHex() : null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1499,7 +1504,7 @@ abstract class ElectrumWalletBase
|
|||
final addressRecord =
|
||||
walletAddresses.allAddresses.firstWhere((element) => element.address == address);
|
||||
|
||||
final btcAddress = addressTypeFromStr(addressRecord.address, network);
|
||||
final btcAddress = RegexUtils.addressTypeFromStr(addressRecord.address, network);
|
||||
final privkey = generateECPrivate(
|
||||
hd: addressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
||||
index: addressRecord.index,
|
||||
|
@ -1540,7 +1545,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
final address = addressFromOutputScript(out.scriptPubKey, network);
|
||||
final btcAddress = addressTypeFromStr(address, network);
|
||||
final btcAddress = RegexUtils.addressTypeFromStr(address, network);
|
||||
outputs.add(BitcoinOutput(address: btcAddress, value: BigInt.from(out.amount.toInt())));
|
||||
}
|
||||
|
||||
|
@ -1625,6 +1630,13 @@ abstract class ElectrumWalletBase
|
|||
hasChange: changeOutputs.isNotEmpty,
|
||||
feeRate: newFee.toString(),
|
||||
)..addListener((transaction) async {
|
||||
transactionHistory.transactions.values.forEach((tx) {
|
||||
if (tx.id == hash) {
|
||||
tx.isReplaced = true;
|
||||
tx.isPending = false;
|
||||
transactionHistory.addOne(tx);
|
||||
}
|
||||
});
|
||||
transactionHistory.addOne(transaction);
|
||||
await updateBalance();
|
||||
});
|
||||
|
@ -1636,8 +1648,6 @@ abstract class ElectrumWalletBase
|
|||
Future<ElectrumTransactionBundle> getTransactionExpanded(
|
||||
{required String hash, int? height}) async {
|
||||
String transactionHex;
|
||||
// TODO: time is not always available, and calculating it from height is not always accurate.
|
||||
// Add settings to choose API provider and use and http server instead of electrum for this.
|
||||
int? time;
|
||||
int? confirmations;
|
||||
|
||||
|
@ -1645,6 +1655,29 @@ abstract class ElectrumWalletBase
|
|||
|
||||
if (verboseTransaction.isEmpty) {
|
||||
transactionHex = await electrumClient.getTransactionHex(hash: hash);
|
||||
|
||||
if (height != null && await checkIfMempoolAPIIsEnabled()) {
|
||||
final blockHash = await http.get(
|
||||
Uri.parse(
|
||||
"http://mempool.cakewallet.com:8999/api/v1/block-height/$height",
|
||||
),
|
||||
);
|
||||
|
||||
if (blockHash.statusCode == 200 &&
|
||||
blockHash.body.isNotEmpty &&
|
||||
jsonDecode(blockHash.body) != null) {
|
||||
final blockResponse = await http.get(
|
||||
Uri.parse(
|
||||
"http://mempool.cakewallet.com:8999/api/v1/block/${blockHash.body}",
|
||||
),
|
||||
);
|
||||
if (blockResponse.statusCode == 200 &&
|
||||
blockResponse.body.isNotEmpty &&
|
||||
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
||||
time = int.parse(jsonDecode(blockResponse.body)['timestamp'].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
transactionHex = verboseTransaction['hex'] as String;
|
||||
time = verboseTransaction['time'] as int?;
|
||||
|
@ -1898,12 +1931,12 @@ abstract class ElectrumWalletBase
|
|||
|
||||
Future<ElectrumBalance> fetchBalances() async {
|
||||
final addresses = walletAddresses.allAddresses
|
||||
.where((address) => addressTypeFromStr(address.address, network) is! MwebAddress)
|
||||
.where((address) => RegexUtils.addressTypeFromStr(address.address, network) is! MwebAddress)
|
||||
.toList();
|
||||
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
||||
for (var i = 0; i < addresses.length; i++) {
|
||||
final addressRecord = addresses[i];
|
||||
final sh = scriptHash(addressRecord.address, network: network);
|
||||
final sh = addressRecord.getScriptHash(network);
|
||||
final balanceFuture = electrumClient.getBalance(sh);
|
||||
balanceFutures.add(balanceFuture);
|
||||
}
|
||||
|
@ -1944,7 +1977,10 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
return ElectrumBalance(
|
||||
confirmed: totalConfirmed, unconfirmed: totalUnconfirmed, frozen: totalFrozen);
|
||||
confirmed: totalConfirmed,
|
||||
unconfirmed: totalUnconfirmed,
|
||||
frozen: totalFrozen,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateBalance() async {
|
||||
|
@ -2000,7 +2036,7 @@ abstract class ElectrumWalletBase
|
|||
|
||||
List<int> possibleRecoverIds = [0, 1];
|
||||
|
||||
final baseAddress = addressTypeFromStr(address, network);
|
||||
final baseAddress = RegexUtils.addressTypeFromStr(address, network);
|
||||
|
||||
for (int recoveryId in possibleRecoverIds) {
|
||||
final pubKey = sig.recoverPublicKey(messageHash, Curves.generatorSecp256k1, recoveryId);
|
||||
|
@ -2225,21 +2261,22 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
|
||||
BehaviorSubject<Object>? tweaksSubscription = null;
|
||||
|
||||
final syncingStatus = scanData.isSingleScan
|
||||
? SyncingSyncStatus(1, 0)
|
||||
: SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight);
|
||||
|
||||
// Initial status UI update, send how many blocks left to scan
|
||||
scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
|
||||
|
||||
final electrumClient = scanData.electrumClient;
|
||||
await electrumClient.connectToUri(
|
||||
scanData.node?.uri ?? Uri.parse("tcp://electrs.cakewallet.com:50001"),
|
||||
useSSL: scanData.node?.useSSL ?? false,
|
||||
);
|
||||
|
||||
int getCountPerRequest(int syncHeight) {
|
||||
if (scanData.isSingleScan) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
final amountLeft = scanData.chainTip - syncHeight + 1;
|
||||
return amountLeft;
|
||||
}
|
||||
|
||||
if (tweaksSubscription == null) {
|
||||
final count = scanData.isSingleScan ? 1 : TWEAKS_COUNT;
|
||||
final receiver = Receiver(
|
||||
scanData.silentAddress.b_scan.toHex(),
|
||||
scanData.silentAddress.B_spend.toHex(),
|
||||
|
@ -2248,16 +2285,45 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
scanData.labelIndexes.length,
|
||||
);
|
||||
|
||||
tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight, count: count);
|
||||
tweaksSubscription?.listen((t) async {
|
||||
final tweaks = t as Map<String, dynamic>;
|
||||
// Initial status UI update, send how many blocks in total to scan
|
||||
final initialCount = getCountPerRequest(syncHeight);
|
||||
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
|
||||
|
||||
if (tweaks["message"] != null) {
|
||||
tweaksSubscription = await electrumClient.tweaksSubscribe(
|
||||
height: syncHeight,
|
||||
count: initialCount,
|
||||
);
|
||||
|
||||
Future<void> listenFn(t) async {
|
||||
final tweaks = t as Map<String, dynamic>;
|
||||
final msg = tweaks["message"];
|
||||
// success or error msg
|
||||
final noData = msg != null;
|
||||
|
||||
if (noData) {
|
||||
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
||||
electrumClient.tweaksSubscribe(height: syncHeight + 1, count: count);
|
||||
final nextHeight = syncHeight + 1;
|
||||
final nextCount = getCountPerRequest(nextHeight);
|
||||
|
||||
if (nextCount > 0) {
|
||||
tweaksSubscription?.close();
|
||||
|
||||
final nextTweaksSubscription = electrumClient.tweaksSubscribe(
|
||||
height: nextHeight,
|
||||
count: nextCount,
|
||||
);
|
||||
nextTweaksSubscription?.listen(listenFn);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Continuous status UI update, send how many blocks left to scan
|
||||
final syncingStatus = scanData.isSingleScan
|
||||
? SyncingSyncStatus(1, 0)
|
||||
: SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight);
|
||||
scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
|
||||
|
||||
final blockHeight = tweaks.keys.first;
|
||||
final tweakHeight = int.parse(blockHeight);
|
||||
|
||||
|
@ -2292,11 +2358,13 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
fee: 0,
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: false,
|
||||
isReplaced: false,
|
||||
date: scanData.network == BitcoinNetwork.mainnet
|
||||
? getDateByBitcoinHeight(tweakHeight)
|
||||
: DateTime.now(),
|
||||
confirmations: scanData.chainTip - tweakHeight + 1,
|
||||
unspents: [],
|
||||
isReceivedSilentPayment: true,
|
||||
);
|
||||
|
||||
addToWallet.forEach((label, value) {
|
||||
|
@ -2351,16 +2419,6 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
} catch (_) {}
|
||||
|
||||
syncHeight = tweakHeight;
|
||||
scanData.sendPort.send(
|
||||
SyncResponse(
|
||||
syncHeight,
|
||||
SyncingSyncStatus.fromHeightValues(
|
||||
scanData.chainTip,
|
||||
initialSyncHeight,
|
||||
syncHeight,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (tweakHeight >= scanData.chainTip || scanData.isSingleScan) {
|
||||
if (tweakHeight >= scanData.chainTip)
|
||||
|
@ -2376,7 +2434,9 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
await tweaksSubscription!.close();
|
||||
await electrumClient.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tweaksSubscription?.listen(listenFn);
|
||||
}
|
||||
|
||||
if (tweaksSubscription == null) {
|
||||
|
@ -2406,6 +2466,8 @@ class EstimatedTxResult {
|
|||
final int fee;
|
||||
final int amount;
|
||||
final bool spendsSilentPayment;
|
||||
|
||||
// final bool sendsToSilentPayment;
|
||||
final bool hasChange;
|
||||
final bool isSendAll;
|
||||
final String? memo;
|
||||
|
@ -2419,33 +2481,6 @@ class PublicKeyWithDerivationPath {
|
|||
final String publicKey;
|
||||
}
|
||||
|
||||
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
||||
if (network is BitcoinCashNetwork) {
|
||||
if (!address.startsWith("bitcoincash:") &&
|
||||
(address.startsWith("q") || address.startsWith("p"))) {
|
||||
address = "bitcoincash:$address";
|
||||
}
|
||||
|
||||
return BitcoinCashAddress(address).baseAddress;
|
||||
}
|
||||
|
||||
if (P2pkhAddress.regex.hasMatch(address)) {
|
||||
return P2pkhAddress.fromAddress(address: address, network: network);
|
||||
} else if (P2shAddress.regex.hasMatch(address)) {
|
||||
return P2shAddress.fromAddress(address: address, network: network);
|
||||
} else if (P2wshAddress.regex.hasMatch(address)) {
|
||||
return P2wshAddress.fromAddress(address: address, network: network);
|
||||
} else if (P2trAddress.regex.hasMatch(address)) {
|
||||
return P2trAddress.fromAddress(address: address, network: network);
|
||||
} else if (MwebAddress.regex.hasMatch(address)) {
|
||||
return MwebAddress.fromAddress(address: address, network: network);
|
||||
} else if (SilentPaymentAddress.regex.hasMatch(address)) {
|
||||
return SilentPaymentAddress.fromAddress(address);
|
||||
} else {
|
||||
return P2wpkhAddress.fromAddress(address: address, network: network);
|
||||
}
|
||||
}
|
||||
|
||||
BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
|
||||
if (type is P2pkhAddress) {
|
||||
return P2pkhAddressType.p2pkh;
|
||||
|
|
|
@ -861,6 +861,34 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000,
|
||||
));
|
||||
final tx2 = BtcTransaction.fromRaw(hex.encode(resp.rawTx));
|
||||
|
||||
// check if any of the inputs of this transaction are hog-ex:
|
||||
tx2.inputs.forEach((txInput) {
|
||||
bool isHogEx = true;
|
||||
|
||||
final utxo = unspentCoins
|
||||
.firstWhere((utxo) => utxo.hash == txInput.txId && utxo.vout == txInput.txIndex);
|
||||
|
||||
if (txInput.sequence.isEmpty) {
|
||||
isHogEx = false;
|
||||
}
|
||||
|
||||
// TODO: detect actual hog-ex inputs
|
||||
// print(txInput.sequence);
|
||||
// print(txInput.txIndex);
|
||||
// print(utxo.value);
|
||||
|
||||
if (!isHogEx) {
|
||||
return;
|
||||
}
|
||||
|
||||
int confirmations = utxo.confirmations ?? 0;
|
||||
if (confirmations < 6) {
|
||||
throw Exception(
|
||||
"A transaction input has less than 6 confirmations, please try again later.");
|
||||
}
|
||||
});
|
||||
|
||||
tx.hexOverride = tx2
|
||||
.copyWith(
|
||||
witnesses: tx2.inputs.asMap().entries.map((e) {
|
||||
|
@ -880,7 +908,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
return TxWitnessInput(stack: [key.signInput(digest), key.getPublic().toHex()]);
|
||||
}).toList())
|
||||
.toHex();
|
||||
tx.outputs = resp.outputId;
|
||||
tx.outputAddresses = resp.outputId;
|
||||
|
||||
return tx
|
||||
..addListener((transaction) async {
|
||||
final addresses = <String>{};
|
||||
|
@ -1041,7 +1070,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
List<int> possibleRecoverIds = [0, 1];
|
||||
|
||||
final baseAddress = addressTypeFromStr(address, network);
|
||||
final baseAddress = RegexUtils.addressTypeFromStr(address, network);
|
||||
|
||||
for (int recoveryId in possibleRecoverIds) {
|
||||
final pubKey = sig.recoverPublicKey(messageHash, Curves.generatorSecp256k1, recoveryId);
|
||||
|
|
|
@ -34,11 +34,11 @@ class LitecoinWalletService extends WalletService<
|
|||
@override
|
||||
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final String mnemonic;
|
||||
switch ( credentials.walletInfo?.derivationInfo?.derivationType) {
|
||||
switch (credentials.walletInfo?.derivationInfo?.derivationType) {
|
||||
case DerivationType.bip39:
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
mnemonic = await MnemonicBip39.generate(strength: strength);
|
||||
mnemonic = credentials.mnemonic ?? await MnemonicBip39.generate(strength: strength);
|
||||
break;
|
||||
case DerivationType.electrum:
|
||||
default:
|
||||
|
|
|
@ -39,7 +39,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
bool isMweb;
|
||||
String? idOverride;
|
||||
String? hexOverride;
|
||||
List<String>? outputs;
|
||||
List<String>? outputAddresses;
|
||||
|
||||
@override
|
||||
String get id => idOverride ?? _tx.txId();
|
||||
|
@ -56,6 +56,19 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
@override
|
||||
int? get outputCount => _tx.outputs.length;
|
||||
|
||||
List<TxOutput> get outputs => _tx.outputs;
|
||||
|
||||
bool get hasSilentPayment => _tx.hasSilentPayment;
|
||||
|
||||
PendingChange? get change {
|
||||
try {
|
||||
final change = _tx.outputs.firstWhere((out) => out.isChange);
|
||||
return PendingChange(change.scriptPubKey.toAddress(), BtcUtils.fromSatoshi(change.amount));
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final List<void Function(ElectrumTransactionInfo transaction)> _listeners;
|
||||
|
||||
Future<void> _commit() async {
|
||||
|
@ -124,8 +137,9 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
isReplaced: false,
|
||||
confirmations: 0,
|
||||
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
|
||||
outputAddresses: outputs,
|
||||
outputAddresses: outputAddresses,
|
||||
fee: fee);
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cw_bitcoin/address_to_output_script.dart';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin;
|
||||
|
||||
String scriptHash(String address, {required bitcoin.BasedUtxoNetwork network}) {
|
||||
final outputScript = addressToOutputScript(address, network);
|
||||
final parts = sha256.convert(outputScript).toString().split('');
|
||||
var res = '';
|
||||
|
||||
for (var i = parts.length - 1; i >= 0; i--) {
|
||||
final char = parts[i];
|
||||
i--;
|
||||
final nextChar = parts[i];
|
||||
res += nextChar;
|
||||
res += char;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
|
@ -78,8 +78,8 @@ packages:
|
|||
dependency: "direct overridden"
|
||||
description:
|
||||
path: "."
|
||||
ref: cake-update-v5
|
||||
resolved-ref: ff2b10eb27b0254ce4518d054332d97d77d9b380
|
||||
ref: cake-update-v7
|
||||
resolved-ref: bc49e3b1cba601828f8ddc3d016188d8c2499088
|
||||
url: "https://github.com/cake-tech/bitcoin_base"
|
||||
source: git
|
||||
version: "4.7.0"
|
||||
|
|
|
@ -63,7 +63,7 @@ dependency_overrides:
|
|||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-update-v6
|
||||
ref: cake-update-v8
|
||||
pointycastle: 3.7.4
|
||||
ffi: 2.1.0
|
||||
|
||||
|
|
|
@ -2,9 +2,21 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class BitcoinCashNewWalletCredentials extends WalletCredentials {
|
||||
BitcoinCashNewWalletCredentials(
|
||||
{required String name, WalletInfo? walletInfo, String? password, String? passphrase})
|
||||
: super(name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
|
||||
BitcoinCashNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
this.mnemonic,
|
||||
String? parentAddress,
|
||||
}) : super(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
parentAddress: parentAddress
|
||||
);
|
||||
final String? mnemonic;
|
||||
}
|
||||
|
||||
class BitcoinCashRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
|
|
@ -36,7 +36,7 @@ class BitcoinCashWalletService extends WalletService<
|
|||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final wallet = await BitcoinCashWalletBase.create(
|
||||
mnemonic: await MnemonicBip39.generate(strength: strength),
|
||||
mnemonic: credentials.mnemonic ?? await MnemonicBip39.generate(strength: strength),
|
||||
password: credentials.password!,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
|
|
|
@ -42,7 +42,7 @@ dependency_overrides:
|
|||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-update-v6
|
||||
ref: cake-update-v8
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
@ -267,6 +267,16 @@ const bitcoinDates = {
|
|||
"2023-01": 769810,
|
||||
};
|
||||
|
||||
Future<int> getBitcoinHeightByDateAPI({required DateTime date}) async {
|
||||
final response = await http.get(
|
||||
Uri.parse(
|
||||
"http://mempool.cakewallet.com:8999/api/v1/mining/blocks/timestamp/${(date.millisecondsSinceEpoch / 1000).round()}",
|
||||
),
|
||||
);
|
||||
|
||||
return jsonDecode(response.body)['height'] as int;
|
||||
}
|
||||
|
||||
int getBitcoinHeightByDate({required DateTime date}) {
|
||||
String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}';
|
||||
final closestKey = bitcoinDates.keys
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
class PendingChange {
|
||||
final String address;
|
||||
final String amount;
|
||||
|
||||
PendingChange(this.address, this.amount);
|
||||
}
|
||||
|
||||
mixin PendingTransaction {
|
||||
String get id;
|
||||
String get amountFormatted;
|
||||
|
@ -5,6 +12,7 @@ mixin PendingTransaction {
|
|||
String? feeRate;
|
||||
String get hex;
|
||||
int? get outputCount => null;
|
||||
PendingChange? change;
|
||||
|
||||
Future<void> commit();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ abstract class SyncStatus {
|
|||
}
|
||||
|
||||
class StartingScanSyncStatus extends SyncStatus {
|
||||
StartingScanSyncStatus(this.beginHeight);
|
||||
|
||||
final int beginHeight;
|
||||
@override
|
||||
double progress() => 0.0;
|
||||
}
|
||||
|
@ -59,6 +62,11 @@ class AttemptingSyncStatus extends SyncStatus {
|
|||
double progress() => 0.0;
|
||||
}
|
||||
|
||||
class AttemptingScanSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 0.0;
|
||||
}
|
||||
|
||||
class FailedSyncStatus extends NotConnectedSyncStatus {}
|
||||
|
||||
class ConnectingSyncStatus extends SyncStatus {
|
||||
|
|
|
@ -18,6 +18,7 @@ abstract class TransactionInfo extends Object with Keyable {
|
|||
String? to;
|
||||
String? from;
|
||||
String? evmSignatureName;
|
||||
bool? isReplaced;
|
||||
List<String>? inputAddresses;
|
||||
List<String>? outputAddresses;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ abstract class WalletCredentials {
|
|||
this.passphrase,
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
this.parentAddress,
|
||||
}) {
|
||||
if (this.walletInfo != null && derivationInfo != null) {
|
||||
this.walletInfo!.derivationInfo = derivationInfo;
|
||||
|
@ -18,6 +19,7 @@ abstract class WalletCredentials {
|
|||
|
||||
final String name;
|
||||
final int? height;
|
||||
String? parentAddress;
|
||||
int? seedPhraseLength;
|
||||
String? password;
|
||||
String? passphrase;
|
||||
|
|
|
@ -80,6 +80,7 @@ class WalletInfo extends HiveObject {
|
|||
this.showIntroCakePayCard,
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
this.parentAddress,
|
||||
) : _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
|
||||
factory WalletInfo.external({
|
||||
|
@ -97,6 +98,7 @@ class WalletInfo extends HiveObject {
|
|||
String yatLastUsedAddressRaw = '',
|
||||
DerivationInfo? derivationInfo,
|
||||
HardwareWalletType? hardwareWalletType,
|
||||
String? parentAddress,
|
||||
}) {
|
||||
return WalletInfo(
|
||||
id,
|
||||
|
@ -113,6 +115,7 @@ class WalletInfo extends HiveObject {
|
|||
showIntroCakePayCard,
|
||||
derivationInfo,
|
||||
hardwareWalletType,
|
||||
parentAddress,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -184,6 +187,9 @@ class WalletInfo extends HiveObject {
|
|||
@HiveField(21)
|
||||
HardwareWalletType? hardwareWalletType;
|
||||
|
||||
@HiveField(22)
|
||||
String? parentAddress;
|
||||
|
||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||
|
||||
set yatLastUsedAddress(String address) {
|
||||
|
|
|
@ -21,7 +21,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
|||
Future<EthereumWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||
final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
|
||||
|
||||
final wallet = EthereumWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
|
|
|
@ -3,8 +3,20 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class EVMChainNewWalletCredentials extends WalletCredentials {
|
||||
EVMChainNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password})
|
||||
: super(name: name, walletInfo: walletInfo, password: password);
|
||||
EVMChainNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? parentAddress,
|
||||
this.mnemonic,
|
||||
}) : super(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
final String? mnemonic;
|
||||
}
|
||||
|
||||
class EVMChainRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
|
|
@ -4,13 +4,19 @@ import 'package:cw_core/wallet_info.dart';
|
|||
class NanoNewWalletCredentials extends WalletCredentials {
|
||||
NanoNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
DerivationType? derivationType,
|
||||
this.mnemonic,
|
||||
String? parentAddress,
|
||||
}) : super(
|
||||
name: name,
|
||||
password: password,
|
||||
derivationInfo: DerivationInfo(derivationType: derivationType),
|
||||
walletInfo: walletInfo,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
final String? mnemonic;
|
||||
}
|
||||
|
||||
class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
|
|
@ -32,15 +32,17 @@ class NanoWalletService extends WalletService<
|
|||
|
||||
@override
|
||||
Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
// nano standard:
|
||||
String seedKey = NanoSeeds.generateSeed();
|
||||
String mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
||||
|
||||
// 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 String mnemonic;
|
||||
switch (credentials.walletInfo?.derivationInfo?.derivationType) {
|
||||
case DerivationType.nano:
|
||||
String seedKey = NanoSeeds.generateSeed();
|
||||
mnemonic = credentials.mnemonic ?? NanoDerivations.standardSeedToMnemonic(seedKey);
|
||||
break;
|
||||
case DerivationType.bip39:
|
||||
default:
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
|
||||
break;
|
||||
}
|
||||
|
||||
final wallet = NanoWallet(
|
||||
|
|
|
@ -117,10 +117,10 @@ packages:
|
|||
dependency: "direct overridden"
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e"
|
||||
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.7+1"
|
||||
version: "7.2.7"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -24,7 +24,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
|||
Future<PolygonWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||
final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
|
||||
|
||||
final wallet = PolygonWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
|
|
|
@ -2,8 +2,19 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class SolanaNewWalletCredentials extends WalletCredentials {
|
||||
SolanaNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password})
|
||||
: super(name: name, walletInfo: walletInfo, password: password);
|
||||
SolanaNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? parentAddress,
|
||||
this.mnemonic,
|
||||
}) : super(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
final String? mnemonic;
|
||||
}
|
||||
|
||||
class SolanaRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
|
|
@ -27,7 +27,7 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
|||
Future<SolanaWallet> create(SolanaNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||
final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
|
||||
|
||||
final wallet = SolanaWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
|
|
|
@ -2,8 +2,20 @@ import 'package:cw_core/wallet_credentials.dart';
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class TronNewWalletCredentials extends WalletCredentials {
|
||||
TronNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password})
|
||||
: super(name: name, walletInfo: walletInfo, password: password);
|
||||
TronNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
this.mnemonic,
|
||||
String? parentAddress,
|
||||
}) : super(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
final String? mnemonic;
|
||||
}
|
||||
|
||||
class TronRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
|
|
|
@ -39,7 +39,7 @@ class TronWalletService extends WalletService<
|
|||
}) async {
|
||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||
|
||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||
final mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
|
||||
|
||||
final wallet = TronWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
|
|
|
@ -3,42 +3,11 @@ PODS:
|
|||
- Flutter
|
||||
- MTBBarcodeScanner
|
||||
- SwiftProtobuf
|
||||
- BigInt (5.2.0)
|
||||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- ReachabilitySwift
|
||||
- CryptoSwift (1.8.2)
|
||||
- cw_mweb (0.0.1):
|
||||
- cw_haven (0.0.1):
|
||||
- cw_haven/Boost (= 0.0.1)
|
||||
- cw_haven/Haven (= 0.0.1)
|
||||
- cw_haven/OpenSSL (= 0.0.1)
|
||||
- cw_haven/Sodium (= 0.0.1)
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_haven/Boost (0.0.1):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_haven/Haven (0.0.1):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_haven/OpenSSL (0.0.1):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_haven/Sodium (0.0.1):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_shared_external (0.0.1):
|
||||
- cw_shared_external/Boost (= 0.0.1)
|
||||
- cw_shared_external/OpenSSL (= 0.0.1)
|
||||
- cw_shared_external/Sodium (= 0.0.1)
|
||||
- Flutter
|
||||
- cw_shared_external/Boost (0.0.1):
|
||||
- Flutter
|
||||
- cw_shared_external/OpenSSL (0.0.1):
|
||||
- Flutter
|
||||
- cw_shared_external/Sodium (0.0.1):
|
||||
- Flutter
|
||||
- device_display_brightness (0.0.1):
|
||||
- Flutter
|
||||
- device_info_plus (0.0.1):
|
||||
|
@ -100,8 +69,6 @@ PODS:
|
|||
- Flutter
|
||||
- MTBBarcodeScanner (5.0.11)
|
||||
- OrderedSet (5.0.0)
|
||||
- package_info (0.0.1):
|
||||
- Flutter
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
|
@ -132,9 +99,6 @@ PODS:
|
|||
- Toast (4.1.1)
|
||||
- uni_links (0.0.1):
|
||||
- Flutter
|
||||
- UnstoppableDomainsResolution (4.0.0):
|
||||
- BigInt
|
||||
- CryptoSwift
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- wakelock_plus (0.0.1):
|
||||
|
@ -147,8 +111,6 @@ DEPENDENCIES:
|
|||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- CryptoSwift
|
||||
- cw_mweb (from `.symlinks/plugins/cw_mweb/ios`)
|
||||
- cw_haven (from `.symlinks/plugins/cw_haven/ios`)
|
||||
- cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`)
|
||||
- device_display_brightness (from `.symlinks/plugins/device_display_brightness/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
|
||||
|
@ -160,7 +122,6 @@ DEPENDENCIES:
|
|||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
|
@ -170,14 +131,12 @@ DEPENDENCIES:
|
|||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sp_scanner (from `.symlinks/plugins/sp_scanner/ios`)
|
||||
- uni_links (from `.symlinks/plugins/uni_links/ios`)
|
||||
- UnstoppableDomainsResolution (~> 4.0.0)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||
- workmanager (from `.symlinks/plugins/workmanager/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
- BigInt
|
||||
- CryptoSwift
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
|
@ -189,7 +148,6 @@ SPEC REPOS:
|
|||
- SwiftProtobuf
|
||||
- SwiftyGif
|
||||
- Toast
|
||||
- UnstoppableDomainsResolution
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
barcode_scan2:
|
||||
|
@ -198,10 +156,6 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
cw_mweb:
|
||||
:path: ".symlinks/plugins/cw_mweb/ios"
|
||||
cw_haven:
|
||||
:path: ".symlinks/plugins/cw_haven/ios"
|
||||
cw_shared_external:
|
||||
:path: ".symlinks/plugins/cw_shared_external/ios"
|
||||
device_display_brightness:
|
||||
:path: ".symlinks/plugins/device_display_brightness/ios"
|
||||
device_info_plus:
|
||||
|
@ -224,8 +178,6 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
in_app_review:
|
||||
:path: ".symlinks/plugins/in_app_review/ios"
|
||||
package_info:
|
||||
:path: ".symlinks/plugins/package_info/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
|
@ -253,12 +205,9 @@ EXTERNAL SOURCES:
|
|||
|
||||
SPEC CHECKSUMS:
|
||||
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
|
||||
BigInt: f668a80089607f521586bbe29513d708491ef2f7
|
||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||
CryptoSwift: c63a805d8bb5e5538e88af4e44bb537776af11ea
|
||||
cw_mweb: 87af74f9659fed0c1a2cbfb44413f1070e79e3ae
|
||||
cw_haven: b3e54e1fbe7b8e6fda57a93206bc38f8e89b898a
|
||||
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
|
||||
device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
devicelocale: b22617f40038496deffba44747101255cee005b0
|
||||
|
@ -274,7 +223,6 @@ SPEC CHECKSUMS:
|
|||
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
|
||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||
|
@ -290,11 +238,10 @@ SPEC CHECKSUMS:
|
|||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
|
||||
UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1
|
||||
workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6
|
||||
|
||||
PODFILE CHECKSUM: a2fe518be61cdbdc5b0e2da085ab543d556af2d3
|
||||
PODFILE CHECKSUM: e448f662d4c41f0c0b1ccbb78afd57dbf895a597
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
|
|
|
@ -28,10 +28,22 @@ class CWBitcoin extends Bitcoin {
|
|||
name: name, password: password, wif: wif, walletInfo: walletInfo);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinNewWalletCredentials(
|
||||
{required String name, WalletInfo? walletInfo, String? password, String? passphrase}) =>
|
||||
WalletCredentials createBitcoinNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
BitcoinNewWalletCredentials(
|
||||
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinHardwareWalletCredentials(
|
||||
|
@ -366,7 +378,7 @@ class CWBitcoin extends Bitcoin {
|
|||
continue;
|
||||
}
|
||||
|
||||
final sh = scriptHash(address, network: network);
|
||||
final sh = BitcoinAddressUtils.scriptHash(address, network: network);
|
||||
final history = await electrumClient.getHistory(sh);
|
||||
|
||||
final balance = await electrumClient.getBalance(sh);
|
||||
|
@ -404,12 +416,18 @@ class CWBitcoin extends Bitcoin {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<bool> canReplaceByFee(Object wallet, Object transactionInfo) async {
|
||||
Future<String?> canReplaceByFee(Object wallet, Object transactionInfo) async {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
final tx = transactionInfo as ElectrumTransactionInfo;
|
||||
return bitcoinWallet.canReplaceByFee(tx);
|
||||
}
|
||||
|
||||
@override
|
||||
int getTransactionVSize(Object wallet, String transactionHex) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
return bitcoinWallet.transactionVSize(transactionHex);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee) async {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
|
@ -527,7 +545,20 @@ class CWBitcoin extends Bitcoin {
|
|||
}
|
||||
|
||||
@override
|
||||
int getHeightByDate({required DateTime date}) => getBitcoinHeightByDate(date: date);
|
||||
Future<bool> checkIfMempoolAPIIsEnabled(Object wallet) async {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
return await bitcoinWallet.checkIfMempoolAPIIsEnabled();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getHeightByDate({required DateTime date, bool? bitcoinMempoolAPIEnabled}) async {
|
||||
if (bitcoinMempoolAPIEnabled ?? false) {
|
||||
try {
|
||||
return await getBitcoinHeightByDateAPI(date: date);
|
||||
} catch (_) {}
|
||||
}
|
||||
return await getBitcoinHeightByDate(date: date);
|
||||
}
|
||||
|
||||
@override
|
||||
int getLitecoinHeightByDate({required DateTime date}) => getLtcHeightByDate(date: date);
|
||||
|
@ -579,4 +610,60 @@ class CWBitcoin extends Bitcoin {
|
|||
final litecoinWallet = wallet as LitecoinWallet;
|
||||
return litecoinWallet.getStatusRequest();
|
||||
}
|
||||
|
||||
List<Output> updateOutputs(PendingTransaction pendingTransaction, List<Output> outputs) {
|
||||
final pendingTx = pendingTransaction as PendingBitcoinTransaction;
|
||||
|
||||
if (!pendingTx.hasSilentPayment) {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
final updatedOutputs = outputs.map((output) {
|
||||
try {
|
||||
final pendingOut = pendingTx!.outputs[outputs.indexOf(output)];
|
||||
final updatedOutput = output;
|
||||
|
||||
updatedOutput.stealthAddress = P2trAddress.fromScriptPubkey(script: pendingOut.scriptPubKey)
|
||||
.toAddress(BitcoinNetwork.mainnet);
|
||||
return updatedOutput;
|
||||
} catch (_) {}
|
||||
|
||||
return output;
|
||||
}).toList();
|
||||
|
||||
return updatedOutputs;
|
||||
}
|
||||
|
||||
@override
|
||||
bool txIsReceivedSilentPayment(TransactionInfo txInfo) {
|
||||
final tx = txInfo as ElectrumTransactionInfo;
|
||||
return tx.isReceivedSilentPayment;
|
||||
}
|
||||
|
||||
@override
|
||||
bool txIsMweb(TransactionInfo txInfo) {
|
||||
final tx = txInfo as ElectrumTransactionInfo;
|
||||
|
||||
List<String> inputAddresses = tx.inputAddresses ?? [];
|
||||
List<String> outputAddresses = tx.outputAddresses ?? [];
|
||||
bool inputAddressesContainMweb = false;
|
||||
bool outputAddressesContainMweb = false;
|
||||
|
||||
for (var address in inputAddresses) {
|
||||
if (address.toLowerCase().contains('mweb')) {
|
||||
inputAddressesContainMweb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var address in outputAddresses) {
|
||||
if (address.toLowerCase().contains('mweb')) {
|
||||
outputAddressesContainMweb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this could be improved:
|
||||
return inputAddressesContainMweb || outputAddressesContainMweb;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,17 @@ class CWBitcoinCash extends BitcoinCash {
|
|||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? passphrase,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
BitcoinCashNewWalletCredentials(
|
||||
name: name, walletInfo: walletInfo, password: password, passphrase: passphrase);
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
parentAddress: parentAddress,
|
||||
mnemonic: mnemonic,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials(
|
||||
|
|
|
@ -5,35 +5,42 @@ import 'package:cake_wallet/solana/solana.dart';
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
|
||||
const BEFORE_REGEX = '(^|\s)';
|
||||
const AFTER_REGEX = '(\$|\s)';
|
||||
|
||||
class AddressValidator extends TextValidator {
|
||||
AddressValidator({required CryptoCurrency type})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: type == CryptoCurrency.btc || type == CryptoCurrency.ltc
|
||||
? (String txt) => validateAddress(address: txt, network:
|
||||
type == CryptoCurrency.btc ? BitcoinNetwork.mainnet : LitecoinNetwork.mainnet)
|
||||
? (String txt) => BitcoinAddressUtils.validateAddress(
|
||||
address: txt,
|
||||
network: type == CryptoCurrency.btc ? BitcoinNetwork.mainnet : LitecoinNetwork.mainnet,
|
||||
)
|
||||
: null,
|
||||
pattern: getPattern(type),
|
||||
length: getLength(type));
|
||||
|
||||
static String getPattern(CryptoCurrency type) {
|
||||
var pattern = "";
|
||||
if (type is Erc20Token) {
|
||||
return '0x[0-9a-zA-Z]';
|
||||
pattern = '0x[0-9a-zA-Z]';
|
||||
}
|
||||
switch (type) {
|
||||
case CryptoCurrency.xmr:
|
||||
return '^4[0-9a-zA-Z]{94}\$|^8[0-9a-zA-Z]{94}\$|^[0-9a-zA-Z]{106}\$';
|
||||
pattern = '4[0-9a-zA-Z]{94}|8[0-9a-zA-Z]{94}|[0-9a-zA-Z]{106}';
|
||||
case CryptoCurrency.ada:
|
||||
return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
|
||||
'|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
|
||||
pattern = '[0-9a-zA-Z]{59}|[0-9a-zA-Z]{92}|[0-9a-zA-Z]{104}'
|
||||
'|[0-9a-zA-Z]{105}|addr1[0-9a-zA-Z]{98}';
|
||||
case CryptoCurrency.btc:
|
||||
return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|^${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${SilentPaymentAddress.regex.pattern}\$';
|
||||
pattern =
|
||||
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
|
||||
case CryptoCurrency.ltc:
|
||||
return '^${P2wpkhAddress.regex.pattern}\$|^${MwebAddress.regex.pattern}\$';
|
||||
case CryptoCurrency.nano:
|
||||
return '[0-9a-zA-Z_]';
|
||||
pattern = '[0-9a-zA-Z_]';
|
||||
case CryptoCurrency.banano:
|
||||
return '[0-9a-zA-Z_]';
|
||||
pattern = '[0-9a-zA-Z_]';
|
||||
case CryptoCurrency.usdc:
|
||||
case CryptoCurrency.usdcpoly:
|
||||
case CryptoCurrency.usdtPoly:
|
||||
|
@ -69,11 +76,11 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.dydx:
|
||||
case CryptoCurrency.steth:
|
||||
case CryptoCurrency.shib:
|
||||
return '0x[0-9a-zA-Z]';
|
||||
pattern = '0x[0-9a-zA-Z]';
|
||||
case CryptoCurrency.xrp:
|
||||
return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$';
|
||||
pattern = '[0-9a-zA-Z]{34}|X[0-9a-zA-Z]{46}';
|
||||
case CryptoCurrency.xhv:
|
||||
return '^hvx|hvi|hvs[0-9a-zA-Z]';
|
||||
pattern = 'hvx|hvi|hvs[0-9a-zA-Z]';
|
||||
case CryptoCurrency.xag:
|
||||
case CryptoCurrency.xau:
|
||||
case CryptoCurrency.xaud:
|
||||
|
@ -95,38 +102,41 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.dash:
|
||||
case CryptoCurrency.eos:
|
||||
case CryptoCurrency.wow:
|
||||
return '[0-9a-zA-Z]';
|
||||
pattern = '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.bch:
|
||||
return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{42}\$|^bitcoincash:q|p[0-9a-zA-Z]{41}\$|^bitcoincash:q|p[0-9a-zA-Z]{42}\$';
|
||||
pattern =
|
||||
'(?!bitcoincash:)[0-9a-zA-Z]*|(?!bitcoincash:)q|p[0-9a-zA-Z]{41}|(?!bitcoincash:)q|p[0-9a-zA-Z]{42}|bitcoincash:q|p[0-9a-zA-Z]{41}|bitcoincash:q|p[0-9a-zA-Z]{42}';
|
||||
case CryptoCurrency.bnb:
|
||||
return '[0-9a-zA-Z]';
|
||||
pattern = '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.hbar:
|
||||
return '[0-9a-zA-Z.]';
|
||||
pattern = '[0-9a-zA-Z.]';
|
||||
case CryptoCurrency.zaddr:
|
||||
return '^zs[0-9a-zA-Z]{75}';
|
||||
pattern = 'zs[0-9a-zA-Z]{75}';
|
||||
case CryptoCurrency.zec:
|
||||
return '^t1[0-9a-zA-Z]{33}\$|^t3[0-9a-zA-Z]{33}\$';
|
||||
pattern = 't1[0-9a-zA-Z]{33}|t3[0-9a-zA-Z]{33}';
|
||||
case CryptoCurrency.dcr:
|
||||
return 'D[ksecS]([0-9a-zA-Z])+';
|
||||
pattern = 'D[ksecS]([0-9a-zA-Z])+';
|
||||
case CryptoCurrency.rvn:
|
||||
return '[Rr]([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
pattern = '[Rr]([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
case CryptoCurrency.near:
|
||||
return '[0-9a-f]{64}';
|
||||
pattern = '[0-9a-f]{64}';
|
||||
case CryptoCurrency.rune:
|
||||
return 'thor1[0-9a-z]{38}';
|
||||
pattern = 'thor1[0-9a-z]{38}';
|
||||
case CryptoCurrency.scrt:
|
||||
return 'secret1[0-9a-z]{38}';
|
||||
pattern = 'secret1[0-9a-z]{38}';
|
||||
case CryptoCurrency.stx:
|
||||
return 'S[MP][0-9a-zA-Z]+';
|
||||
pattern = 'S[MP][0-9a-zA-Z]+';
|
||||
case CryptoCurrency.kmd:
|
||||
return 'R[0-9a-zA-Z]{33}';
|
||||
pattern = 'R[0-9a-zA-Z]{33}';
|
||||
case CryptoCurrency.pivx:
|
||||
return 'D([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
pattern = 'D([1-9a-km-zA-HJ-NP-Z]){33}';
|
||||
case CryptoCurrency.btcln:
|
||||
return '^(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)';
|
||||
pattern = '(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)';
|
||||
default:
|
||||
return '[0-9a-zA-Z]';
|
||||
pattern = '[0-9a-zA-Z]';
|
||||
}
|
||||
|
||||
return '$BEFORE_REGEX$pattern$AFTER_REGEX';
|
||||
}
|
||||
|
||||
static List<int>? getLength(CryptoCurrency type) {
|
||||
|
@ -267,57 +277,55 @@ class AddressValidator extends TextValidator {
|
|||
}
|
||||
|
||||
static String? getAddressFromStringPattern(CryptoCurrency type) {
|
||||
String? pattern = null;
|
||||
|
||||
switch (type) {
|
||||
case CryptoCurrency.xmr:
|
||||
case CryptoCurrency.wow:
|
||||
return '([^0-9a-zA-Z]|^)4[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)';
|
||||
pattern = '4[0-9a-zA-Z]{94}'
|
||||
'|8[0-9a-zA-Z]{94}'
|
||||
'|[0-9a-zA-Z]{106}';
|
||||
case CryptoCurrency.btc:
|
||||
return '([^0-9a-zA-Z]|^)([1mn][a-km-zA-HJ-NP-Z1-9]{25,34})([^0-9a-zA-Z]|\$)' //P2pkhAddress type
|
||||
'|([^0-9a-zA-Z]|^)([23][a-km-zA-HJ-NP-Z1-9]{25,34})([^0-9a-zA-Z]|\$)' //P2shAddress type
|
||||
'|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{25,39})([^0-9a-zA-Z]|\$)' //P2wpkhAddress type
|
||||
'|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type
|
||||
'|([^0-9a-zA-Z]|^)((bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))([^0-9a-zA-Z]|\$)' //P2trAddress type
|
||||
'|${SilentPaymentAddress.regex.pattern}\$';
|
||||
|
||||
pattern =
|
||||
'${P2pkhAddress.regex.pattern}|${P2shAddress.regex.pattern}|${P2wpkhAddress.regex.pattern}|${P2trAddress.regex.pattern}|${P2wshAddress.regex.pattern}|${SilentPaymentAddress.regex.pattern}';
|
||||
case CryptoCurrency.ltc:
|
||||
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)ltc[a-zA-Z0-9]{26,45}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)((ltc|t)mweb1q[ac-hj-np-z02-9]{90,120})([^0-9a-zA-Z]|\$)';
|
||||
pattern = '^L[a-zA-Z0-9]{26,33}'
|
||||
'|[LM][a-km-zA-HJ-NP-Z1-9]{26,33}'
|
||||
'|ltc[a-zA-Z0-9]{26,45}'
|
||||
'|([^0-9a-zA-Z]|^)((ltc|t)mweb1q[ac-hj-np-z02-9]{90,120}';
|
||||
case CryptoCurrency.eth:
|
||||
return '0x[0-9a-zA-Z]{42}';
|
||||
pattern = '0x[0-9a-zA-Z]{42}';
|
||||
case CryptoCurrency.maticpoly:
|
||||
return '0x[0-9a-zA-Z]{42}';
|
||||
pattern = '0x[0-9a-zA-Z]{42}';
|
||||
case CryptoCurrency.nano:
|
||||
return 'nano_[0-9a-zA-Z]{60}';
|
||||
pattern = 'nano_[0-9a-zA-Z]{60}';
|
||||
case CryptoCurrency.banano:
|
||||
return 'ban_[0-9a-zA-Z]{60}';
|
||||
pattern = 'ban_[0-9a-zA-Z]{60}';
|
||||
case CryptoCurrency.bch:
|
||||
return 'bitcoincash:q[0-9a-zA-Z]{41}([^0-9a-zA-Z]|\$)'
|
||||
'|bitcoincash:q[0-9a-zA-Z]{42}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)q[0-9a-zA-Z]{41}([^0-9a-zA-Z]|\$)'
|
||||
'|([^0-9a-zA-Z]|^)q[0-9a-zA-Z]{42}([^0-9a-zA-Z]|\$)';
|
||||
pattern = '(bitcoincash:)?q[0-9a-zA-Z]{41,42}';
|
||||
case CryptoCurrency.sol:
|
||||
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
|
||||
pattern = '[1-9A-HJ-NP-Za-km-z]{43,44}';
|
||||
case CryptoCurrency.trx:
|
||||
return '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
|
||||
pattern = '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
|
||||
default:
|
||||
if (type.tag == CryptoCurrency.eth.title) {
|
||||
return '0x[0-9a-zA-Z]{42}';
|
||||
pattern = '0x[0-9a-zA-Z]{42}';
|
||||
}
|
||||
if (type.tag == CryptoCurrency.maticpoly.tag) {
|
||||
return '0x[0-9a-zA-Z]{42}';
|
||||
pattern = '0x[0-9a-zA-Z]{42}';
|
||||
}
|
||||
if (type.tag == CryptoCurrency.sol.title) {
|
||||
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
|
||||
pattern = '[1-9A-HJ-NP-Za-km-z]{43,44}';
|
||||
}
|
||||
if (type.tag == CryptoCurrency.trx.title) {
|
||||
return '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
|
||||
pattern = '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pattern != null) {
|
||||
return "$BEFORE_REGEX$pattern$AFTER_REGEX";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
15
lib/core/new_wallet_arguments.dart
Normal file
15
lib/core/new_wallet_arguments.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class NewWalletArguments {
|
||||
final WalletType type;
|
||||
final String? mnemonic;
|
||||
final String? parentAddress;
|
||||
final bool isChildWallet;
|
||||
|
||||
NewWalletArguments({
|
||||
required this.type,
|
||||
this.parentAddress,
|
||||
this.mnemonic,
|
||||
this.isChildWallet = false,
|
||||
});
|
||||
}
|
14
lib/core/new_wallet_type_arguments.dart
Normal file
14
lib/core/new_wallet_type_arguments.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NewWalletTypeArguments {
|
||||
final void Function(BuildContext, WalletType)? onTypeSelected;
|
||||
final bool isCreate;
|
||||
final bool isHardwareWallet;
|
||||
|
||||
NewWalletTypeArguments({
|
||||
required this.onTypeSelected,
|
||||
required this.isCreate,
|
||||
required this.isHardwareWallet,
|
||||
});
|
||||
}
|
|
@ -53,7 +53,11 @@ String syncStatusTitle(SyncStatus syncStatus) {
|
|||
}
|
||||
|
||||
if (syncStatus is StartingScanSyncStatus) {
|
||||
return S.current.sync_status_starting_scan;
|
||||
return S.current.sync_status_starting_scan(syncStatus.beginHeight.toString());
|
||||
}
|
||||
|
||||
if (syncStatus is AttemptingScanSyncStatus) {
|
||||
return S.current.sync_status_attempting_scan;
|
||||
}
|
||||
|
||||
return '';
|
||||
|
|
122
lib/di.dart
122
lib/di.dart
|
@ -12,10 +12,12 @@ import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
|||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/backup_service.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'package:cake_wallet/core/secure_storage.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
|
||||
|
@ -30,6 +32,8 @@ import 'package:cake_wallet/entities/contact.dart';
|
|||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/address_list_page.dart';
|
||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||
import 'package:cake_wallet/tron/tron.dart';
|
||||
|
@ -146,7 +150,9 @@ import 'package:cake_wallet/view_model/cake_pay/cake_pay_cards_list_view_model.d
|
|||
import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_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/new_wallet_type_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_groups_display_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/restore/restore_from_qr_vm.dart';
|
||||
|
@ -159,7 +165,6 @@ import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart
|
|||
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
|
||||
import 'package:cw_core/nano_account.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
|
@ -364,13 +369,31 @@ Future<void> setup({
|
|||
getIt.get<KeyService>(),
|
||||
(WalletType type) => getIt.get<WalletService>(param1: type)));
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) => WalletNewVM(
|
||||
getIt.registerFactoryParam<WalletNewVM, NewWalletArguments, void>(
|
||||
(newWalletArgs, _) => WalletNewVM(
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type),
|
||||
getIt.get<WalletCreationService>(param1:newWalletArgs.type),
|
||||
_walletInfoSource,
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: newWalletArgs.type),
|
||||
getIt.get<SeedSettingsViewModel>(),
|
||||
type: type));
|
||||
newWalletArguments: newWalletArgs,));
|
||||
|
||||
|
||||
getIt.registerFactory<NewWalletTypeViewModel>(() => NewWalletTypeViewModel(_walletInfoSource));
|
||||
|
||||
getIt.registerFactory<WalletManager>(
|
||||
() => WalletManager(_walletInfoSource, getIt.get<SharedPreferences>()),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<WalletGroupsDisplayViewModel, WalletType, void>(
|
||||
(type, _) => WalletGroupsDisplayViewModel(
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
getIt.get<WalletListViewModel>(),
|
||||
type: type,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) {
|
||||
return WalletUnlockPage(getIt.get<WalletUnlockLoadableViewModel>(param1: args), args.callback,
|
||||
|
@ -720,6 +743,7 @@ Future<void> setup({
|
|||
_walletInfoSource,
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -730,6 +754,7 @@ Future<void> setup({
|
|||
_walletInfoSource,
|
||||
getIt.get<AppStore>(),
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -740,17 +765,28 @@ Future<void> setup({
|
|||
));
|
||||
|
||||
getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
|
||||
(WalletListViewModel walletListViewModel, _) =>
|
||||
WalletEditViewModel(walletListViewModel, getIt.get<WalletLoadingService>()));
|
||||
(WalletListViewModel walletListViewModel, _) => WalletEditViewModel(
|
||||
walletListViewModel,
|
||||
getIt.get<WalletLoadingService>(),
|
||||
getIt.get<WalletManager>(),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<WalletEditPage, WalletEditPageArguments, void>((arguments, _) {
|
||||
|
||||
getIt.registerFactoryParam<WalletEditPage, List<dynamic>, void>((args, _) {
|
||||
final walletListViewModel = args.first as WalletListViewModel;
|
||||
final editingWallet = args.last as WalletListItem;
|
||||
return WalletEditPage(
|
||||
walletEditViewModel: getIt.get<WalletEditViewModel>(param1: walletListViewModel),
|
||||
pageArguments: WalletEditPageArguments(
|
||||
walletEditViewModel: getIt.get<WalletEditViewModel>(param1: arguments.walletListViewModel),
|
||||
authService: getIt.get<AuthService>(),
|
||||
walletNewVM: getIt.get<WalletNewVM>(param1: editingWallet.type),
|
||||
editingWallet: editingWallet);
|
||||
walletNewVM: getIt.get<WalletNewVM>(
|
||||
param1: NewWalletArguments(type: arguments.editingWallet.type),
|
||||
),
|
||||
editingWallet: arguments.editingWallet,
|
||||
isWalletGroup: arguments.isWalletGroup,
|
||||
groupName: arguments.groupName,
|
||||
parentAddress: arguments.parentAddress,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
getIt.registerFactory<NanoAccountListViewModel>(() {
|
||||
|
@ -1056,31 +1092,46 @@ Future<void> setup({
|
|||
param1: derivations,
|
||||
)));
|
||||
|
||||
getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
|
||||
(TransactionInfo transactionInfo, _) {
|
||||
final wallet = getIt.get<AppStore>().wallet!;
|
||||
return TransactionDetailsViewModel(
|
||||
transactionInfo: transactionInfo,
|
||||
transactionDescriptionBox: _transactionDescriptionBox,
|
||||
wallet: wallet,
|
||||
settingsStore: getIt.get<SettingsStore>(),
|
||||
sendViewModel: getIt.get<SendViewModel>());
|
||||
});
|
||||
getIt.registerFactoryParam<TransactionDetailsViewModel, List<dynamic>, void>(
|
||||
(params, _) {
|
||||
final transactionInfo = params[0] as TransactionInfo;
|
||||
final canReplaceByFee = params[1] as bool? ?? false;
|
||||
final wallet = getIt.get<AppStore>().wallet!;
|
||||
|
||||
return TransactionDetailsViewModel(
|
||||
transactionInfo: transactionInfo,
|
||||
transactionDescriptionBox: _transactionDescriptionBox,
|
||||
wallet: wallet,
|
||||
settingsStore: getIt.get<SettingsStore>(),
|
||||
sendViewModel: getIt.get<SendViewModel>(),
|
||||
canReplaceByFee: canReplaceByFee,
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
|
||||
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
|
||||
transactionDetailsViewModel:
|
||||
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
||||
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
|
||||
transactionDetailsViewModel: getIt.get<TransactionDetailsViewModel>(
|
||||
param1: [transactionInfo, false])));
|
||||
|
||||
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType),
|
||||
List<bool>?>((param1, additionalParams) {
|
||||
final isCreate = additionalParams?[0] ?? true;
|
||||
final isHardwareWallet = additionalParams?[1] ?? false;
|
||||
getIt.registerFactoryParam<RBFDetailsPage, List<dynamic>, void>(
|
||||
(params, _) {
|
||||
final transactionInfo = params[0] as TransactionInfo;
|
||||
final txHex = params[1] as String;
|
||||
return RBFDetailsPage(
|
||||
transactionDetailsViewModel: getIt.get<TransactionDetailsViewModel>(
|
||||
param1: [transactionInfo, true],
|
||||
),
|
||||
rawTransaction: txHex,
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<NewWalletTypePage, NewWalletTypeArguments, void>(
|
||||
(newWalletTypeArguments, _) {
|
||||
return NewWalletTypePage(
|
||||
onTypeSelected: param1,
|
||||
isCreate: isCreate,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
newWalletTypeArguments: newWalletTypeArguments,
|
||||
newWalletTypeViewModel: getIt.get<NewWalletTypeViewModel>(),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1246,11 +1297,6 @@ Future<void> setup({
|
|||
|
||||
getIt.registerFactory(() => CakePayAccountPage(getIt.get<CakePayAccountViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<RBFDetailsPage, TransactionInfo, void>(
|
||||
(TransactionInfo transactionInfo, _) => RBFDetailsPage(
|
||||
transactionDetailsViewModel:
|
||||
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
||||
|
||||
getIt.registerFactory(() => AnonPayApi(
|
||||
useTorOnly: getIt.get<SettingsStore>().exchangeStatus == ExchangeApiMode.torOnly,
|
||||
wallet: getIt.get<AppStore>().wallet!));
|
||||
|
|
|
@ -51,7 +51,7 @@ class AddressResolver {
|
|||
throw Exception('Unexpected token: $type for getAddressFromStringPattern');
|
||||
}
|
||||
|
||||
final match = RegExp(addressPattern).firstMatch(raw);
|
||||
final match = RegExp(addressPattern, multiLine: true).firstMatch(raw);
|
||||
return match?.group(0)?.replaceAllMapped(RegExp('[^0-9a-zA-Z]|bitcoincash:|nano_|ban_'),
|
||||
(Match match) {
|
||||
String group = match.group(0)!;
|
||||
|
@ -213,8 +213,7 @@ class AddressResolver {
|
|||
await NostrProfileHandler.processRelays(context, nostrProfile!, text);
|
||||
|
||||
if (nostrUserData != null) {
|
||||
String? addressFromBio = extractAddressByType(
|
||||
raw: nostrUserData.about, type: currency);
|
||||
String? addressFromBio = extractAddressByType(raw: nostrUserData.about, type: currency);
|
||||
if (addressFromBio != null) {
|
||||
return ParsedAddress.nostrAddress(
|
||||
address: addressFromBio,
|
||||
|
|
|
@ -84,6 +84,7 @@ class PreferencesKey {
|
|||
static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status';
|
||||
static const moneroSeedType = 'monero_seed_type';
|
||||
static const bitcoinSeedType = 'bitcoin_seed_type';
|
||||
static const nanoSeedType = 'nano_seed_type';
|
||||
static const clearnetDonationLink = 'clearnet_donation_link';
|
||||
static const onionDonationLink = 'onion_donation_link';
|
||||
static const donationLinkWalletName = 'donation_link_wallet_name';
|
||||
|
|
|
@ -65,3 +65,28 @@ class BitcoinSeedType extends EnumerableItem<int> with Serializable<int> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NanoSeedType extends EnumerableItem<int> with Serializable<int> {
|
||||
const NanoSeedType(this.type, {required String title, required int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
final DerivationType type;
|
||||
|
||||
static const all = [NanoSeedType.nanoStandard, NanoSeedType.bip39];
|
||||
|
||||
static const defaultDerivationType = bip39;
|
||||
|
||||
static const nanoStandard = NanoSeedType(DerivationType.nano, raw: 0, title: 'Nano');
|
||||
static const bip39 = NanoSeedType(DerivationType.bip39, raw: 1, title: 'BIP39');
|
||||
|
||||
static NanoSeedType deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return nanoStandard;
|
||||
case 1:
|
||||
return bip39;
|
||||
default:
|
||||
throw Exception('Unexpected token: $raw for SeedType deserialize');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
lib/entities/wallet_edit_page_arguments.dart
Normal file
28
lib/entities/wallet_edit_page_arguments.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
|
||||
class WalletEditPageArguments {
|
||||
WalletEditPageArguments({
|
||||
required this.editingWallet,
|
||||
this.isWalletGroup = false,
|
||||
this.walletListViewModel,
|
||||
this.groupName = '',
|
||||
this.parentAddress = '',
|
||||
this.walletEditViewModel,
|
||||
this.walletNewVM,
|
||||
this.authService,
|
||||
});
|
||||
|
||||
final WalletListItem editingWallet;
|
||||
final bool isWalletGroup;
|
||||
final String groupName;
|
||||
final String parentAddress;
|
||||
final WalletListViewModel? walletListViewModel;
|
||||
|
||||
final WalletEditViewModel? walletEditViewModel;
|
||||
final WalletNewVM? walletNewVM;
|
||||
final AuthService? authService;
|
||||
}
|
21
lib/entities/wallet_group.dart
Normal file
21
lib/entities/wallet_group.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class WalletGroup {
|
||||
WalletGroup(this.parentAddress) : wallets = [];
|
||||
|
||||
/// Main identifier for each group, compulsory.
|
||||
final String parentAddress;
|
||||
|
||||
/// Child wallets that share the same parent address within this group
|
||||
List<WalletInfo> wallets;
|
||||
|
||||
/// Custom name for the group, editable for multi-child wallet groups
|
||||
String? groupName;
|
||||
|
||||
/// Allows editing of the group name (only for multi-child groups).
|
||||
void setCustomName(String name) {
|
||||
if (wallets.length > 1) {
|
||||
groupName = name;
|
||||
}
|
||||
}
|
||||
}
|
110
lib/entities/wallet_manager.dart
Normal file
110
lib/entities/wallet_manager.dart
Normal file
|
@ -0,0 +1,110 @@
|
|||
import 'package:cake_wallet/entities/wallet_group.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class WalletManager {
|
||||
WalletManager(
|
||||
this._walletInfoSource,
|
||||
this._sharedPreferences,
|
||||
);
|
||||
|
||||
final Box<WalletInfo> _walletInfoSource;
|
||||
final SharedPreferences _sharedPreferences;
|
||||
|
||||
final List<WalletGroup> walletGroups = [];
|
||||
|
||||
/// Categorize wallets into groups based on their parentAddress.
|
||||
///
|
||||
/// Update the lead wallet for each group and clean up empty groups
|
||||
/// i.e remove group if there's no lead wallet (i.e, no wallets left)
|
||||
void updateWalletGroups() {
|
||||
walletGroups.clear();
|
||||
|
||||
for (var walletInfo in _walletInfoSource.values) {
|
||||
final group = _getOrCreateGroup(_resolveParentAddress(walletInfo));
|
||||
group.wallets.add(walletInfo);
|
||||
}
|
||||
|
||||
walletGroups.removeWhere((group) => group.wallets.isEmpty);
|
||||
|
||||
_loadCustomGroupNames();
|
||||
}
|
||||
|
||||
/// Function to determine the correct parentAddress for a wallet.
|
||||
///
|
||||
/// If it's a parent wallet (parentAddress is null),
|
||||
/// use its own address as parentAddress.
|
||||
String _resolveParentAddress(WalletInfo walletInfo) {
|
||||
return walletInfo.parentAddress ?? walletInfo.address;
|
||||
}
|
||||
|
||||
/// Check if a group with the parentAddress already exists,
|
||||
/// If no group exists, create a new one.
|
||||
///
|
||||
WalletGroup _getOrCreateGroup(String parentAddress) {
|
||||
return walletGroups.firstWhere(
|
||||
(group) => group.parentAddress == parentAddress,
|
||||
orElse: () {
|
||||
final newGroup = WalletGroup(parentAddress);
|
||||
walletGroups.add(newGroup);
|
||||
return newGroup;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a new wallet and update lead wallet after adding.
|
||||
void addWallet(WalletInfo walletInfo) {
|
||||
final group = _getOrCreateGroup(_resolveParentAddress(walletInfo));
|
||||
group.wallets.add(walletInfo);
|
||||
}
|
||||
|
||||
/// Removes a wallet from a group i.e when it's deleted.
|
||||
///
|
||||
/// Update lead wallet after removing,
|
||||
/// Remove the group if it's empty (i.e., no lead wallet).
|
||||
void removeWallet(WalletInfo walletInfo) {
|
||||
final group = _getOrCreateGroup(_resolveParentAddress(walletInfo));
|
||||
group.wallets.remove(walletInfo);
|
||||
|
||||
if (group.wallets.isEmpty) {
|
||||
walletGroups.remove(group);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all the child wallets within a group.
|
||||
///
|
||||
/// If the group is not found, returns an empty group with no wallets.
|
||||
List<WalletInfo> getWalletsInGroup(String parentAddress) {
|
||||
return walletGroups
|
||||
.firstWhere(
|
||||
(group) => group.parentAddress == parentAddress,
|
||||
orElse: () => WalletGroup(parentAddress),
|
||||
)
|
||||
.wallets;
|
||||
}
|
||||
|
||||
/// Iterate through all groups and load their custom names from storage
|
||||
void _loadCustomGroupNames() {
|
||||
for (var group in walletGroups) {
|
||||
final groupName = _sharedPreferences.getString('wallet_group_name_${group.parentAddress}');
|
||||
if (groupName != null && group.wallets.length > 1) {
|
||||
group.groupName = groupName; // Restore custom name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Save custom name for a group
|
||||
void _saveCustomGroupName(String parentAddress, String name) {
|
||||
_sharedPreferences.setString('wallet_group_name_$parentAddress', name);
|
||||
}
|
||||
|
||||
// Set custom group name and persist it
|
||||
void setGroupName(String parentAddress, String name) {
|
||||
if (parentAddress.isEmpty || name.isEmpty) return;
|
||||
|
||||
final group = walletGroups.firstWhere((group) => group.parentAddress == parentAddress);
|
||||
group.setCustomName(name);
|
||||
_saveCustomGroupName(parentAddress, name); // Persist the custom name
|
||||
}
|
||||
}
|
|
@ -10,10 +10,18 @@ class CWEthereum extends Ethereum {
|
|||
@override
|
||||
WalletCredentials createEthereumNewWalletCredentials({
|
||||
required String name,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
}) =>
|
||||
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
parentAddress: parentAddress,
|
||||
mnemonic: mnemonic,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createEthereumRestoreWalletFromSeedCredentials({
|
||||
|
|
|
@ -91,12 +91,17 @@ class CWNano extends Nano {
|
|||
@override
|
||||
WalletCredentials createNanoNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
NanoNewWalletCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
derivationType: DerivationType.nano,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
@override
|
||||
|
|
|
@ -8,12 +8,19 @@ class CWPolygon extends Polygon {
|
|||
PolygonWalletService(walletInfoSource, isDirect, client: PolygonClient());
|
||||
|
||||
@override
|
||||
WalletCredentials createPolygonNewWalletCredentials({
|
||||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password
|
||||
}) =>
|
||||
EVMChainNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
WalletCredentials createPolygonNewWalletCredentials(
|
||||
{required String name,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
WalletInfo? walletInfo,
|
||||
String? password}) =>
|
||||
EVMChainNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createPolygonRestoreWalletFromSeedCredentials({
|
||||
|
@ -77,21 +84,21 @@ class CWPolygon extends Polygon {
|
|||
int? feeRate,
|
||||
}) =>
|
||||
EVMChainTransactionCredentials(
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
cryptoAmount: out.cryptoAmount,
|
||||
address: out.address,
|
||||
note: out.note,
|
||||
sendAll: out.sendAll,
|
||||
extractedAddress: out.extractedAddress,
|
||||
isParsedAddress: out.isParsedAddress,
|
||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||
.toList(),
|
||||
priority: priority as EVMChainTransactionPriority,
|
||||
currency: currency,
|
||||
feeRate: feeRate,
|
||||
);
|
||||
|
||||
Object createPolygonTransactionCredentialsRaw(
|
||||
List<OutputInfo> outputs, {
|
||||
|
|
21
lib/reactions/bip39_wallet_utils.dart
Normal file
21
lib/reactions/bip39_wallet_utils.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
bool isBIP39Wallet(WalletType walletType) {
|
||||
switch (walletType) {
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
case WalletType.solana:
|
||||
case WalletType.tron:
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return true;
|
||||
case WalletType.monero:
|
||||
case WalletType.wownero:
|
||||
case WalletType.haven:
|
||||
case WalletType.none:
|
||||
return false;
|
||||
}
|
||||
}
|
132
lib/router.dart
132
lib/router.dart
|
@ -1,11 +1,14 @@
|
|||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_nft_response.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -40,9 +43,11 @@ import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
|||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/wallet_group_display_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
|
||||
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/wallet_group_description_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/order_details/order_details_page.dart';
|
||||
|
@ -105,6 +110,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/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/wallet_groups_display_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_new_vm.dart';
|
||||
|
@ -135,7 +141,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
if (availableWalletTypes.length == 1) {
|
||||
return createRoute(
|
||||
RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first));
|
||||
RouteSettings(
|
||||
name: Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: availableWalletTypes.first),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return createRoute(RouteSettings(name: Routes.newWalletType));
|
||||
}
|
||||
|
@ -145,8 +155,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
builder: (_) =>
|
||||
getIt.get<SetupPinCodePage>(param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
|
||||
if (availableWalletTypes.length == 1) {
|
||||
Navigator.of(context.context)
|
||||
.pushNamed(Routes.newWallet, arguments: availableWalletTypes.first);
|
||||
Navigator.of(context.context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: availableWalletTypes.first),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context.context).pushNamed(Routes.newWalletType);
|
||||
}
|
||||
|
@ -155,17 +167,38 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
|
||||
case Routes.newWalletType:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.newWallet, arguments: type)));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: type),
|
||||
),
|
||||
isCreate: true,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.walletGroupsDisplayPage:
|
||||
final type = settings.arguments as WalletType;
|
||||
final walletGroupsDisplayVM = getIt.get<WalletGroupsDisplayViewModel>(param1: type);
|
||||
|
||||
return CupertinoPageRoute<void>(builder: (_) => WalletGroupsDisplayPage(walletGroupsDisplayVM));
|
||||
|
||||
case Routes.newWallet:
|
||||
final type = settings.arguments as WalletType;
|
||||
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
||||
final args = settings.arguments as NewWalletArguments;
|
||||
|
||||
final walletNewVM = getIt.get<WalletNewVM>(param1: args);
|
||||
final seedSettingsViewModel = getIt.get<SeedSettingsViewModel>();
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => NewWalletPage(walletNewVM, seedSettingsViewModel));
|
||||
builder: (_) => NewWalletPage(
|
||||
walletNewVM,
|
||||
seedSettingsViewModel,
|
||||
isChildWallet: args.isChildWallet,
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.chooseHardwareWalletAccount:
|
||||
final arguments = settings.arguments as List<dynamic>;
|
||||
|
@ -186,10 +219,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
|
||||
case Routes.restoreWalletType:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
param2: [false, false]));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
isCreate: false,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.restoreOptions:
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
|
@ -221,10 +259,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
builder: (_) => getIt.get<WalletRestorePage>(param1: availableWalletTypes.first));
|
||||
} else {
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
param2: [false, false]));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||
isCreate: false,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
case Routes.restoreWalletFromHardwareWallet:
|
||||
|
@ -253,23 +296,35 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
));
|
||||
} else {
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) {
|
||||
final arguments = ConnectDevicePageParams(
|
||||
walletType: type,
|
||||
onConnectDevice: (BuildContext context, _) => Navigator.of(context)
|
||||
.pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
|
||||
);
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) {
|
||||
final arguments = ConnectDevicePageParams(
|
||||
walletType: type,
|
||||
onConnectDevice: (BuildContext context, _) => Navigator.of(context)
|
||||
.pushNamed(Routes.chooseHardwareWalletAccount, arguments: [type]),
|
||||
);
|
||||
|
||||
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
|
||||
},
|
||||
param2: [false, true]));
|
||||
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
|
||||
},
|
||||
isCreate: false,
|
||||
isHardwareWallet: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
case Routes.restoreWalletTypeFromQR:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: (BuildContext context, WalletType type) => Navigator.of(context).pop(type)));
|
||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) =>
|
||||
Navigator.of(context).pop(type),
|
||||
isCreate: false,
|
||||
isHardwareWallet: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.seed:
|
||||
return MaterialPageRoute<void>(
|
||||
|
@ -321,7 +376,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
case Routes.bumpFeePage:
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => getIt.get<RBFDetailsPage>(param1: settings.arguments as TransactionInfo));
|
||||
builder: (_) => getIt.get<RBFDetailsPage>(param1: settings.arguments as List<dynamic>));
|
||||
|
||||
case Routes.newSubaddress:
|
||||
return CupertinoPageRoute<void>(
|
||||
|
@ -342,8 +397,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
|
||||
case Routes.walletEdit:
|
||||
return MaterialPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => getIt.get<WalletEditPage>(param1: settings.arguments as List<dynamic>));
|
||||
fullscreenDialog: true,
|
||||
builder: (_) =>
|
||||
getIt.get<WalletEditPage>(param1: settings.arguments as WalletEditPageArguments),
|
||||
);
|
||||
|
||||
case Routes.auth:
|
||||
return MaterialPageRoute<void>(
|
||||
|
@ -597,12 +654,14 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
final args = settings.arguments as Map<String, dynamic>;
|
||||
final type = args['type'] as WalletType;
|
||||
final isFromRestore = args['isFromRestore'] as bool? ?? false;
|
||||
final isChildWallet = args['isChildWallet'] as bool? ?? false;
|
||||
final useTestnet = args['useTestnet'] as bool;
|
||||
final toggleTestnet = args['toggleTestnet'] as Function(bool? val);
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => AdvancedPrivacySettingsPage(
|
||||
isFromRestore: isFromRestore,
|
||||
isChildWallet: isChildWallet,
|
||||
useTestnet: useTestnet,
|
||||
toggleUseTestnet: toggleTestnet,
|
||||
advancedPrivacySettingsViewModel:
|
||||
|
@ -717,6 +776,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
return MaterialPageRoute<void>(
|
||||
builder: (_) => ConnectDevicePage(params, getIt.get<LedgerViewModel>()));
|
||||
|
||||
case Routes.walletGroupDescription:
|
||||
final walletType = settings.arguments as WalletType;
|
||||
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => WalletGroupDescriptionPage(
|
||||
selectedWalletType: walletType,
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => Scaffold(
|
||||
|
|
|
@ -8,8 +8,7 @@ class Routes {
|
|||
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
|
||||
static const restoreWalletFromHardwareWallet = '/restore/hardware_wallet';
|
||||
static const restoreWalletTypeFromQR = '/restore_wallet_from_qr_code';
|
||||
static const restoreWalletChooseDerivation =
|
||||
'/restore_wallet_choose_derivation';
|
||||
static const restoreWalletChooseDerivation = '/restore_wallet_choose_derivation';
|
||||
static const chooseHardwareWalletAccount = '/restore/hardware_wallet/accounts';
|
||||
static const dashboard = '/dashboard';
|
||||
static const send = '/send';
|
||||
|
@ -100,11 +99,13 @@ class Routes {
|
|||
static const editToken = '/edit_token';
|
||||
static const manageNodes = '/manage_nodes';
|
||||
static const managePowNodes = '/manage_pow_nodes';
|
||||
static const walletConnectConnectionsListing =
|
||||
'/wallet-connect-connections-listing';
|
||||
static const walletConnectConnectionsListing = '/wallet-connect-connections-listing';
|
||||
static const nftDetailsPage = '/nft_details_page';
|
||||
static const importNFTPage = '/import_nft_page';
|
||||
static const torPage = '/tor_page';
|
||||
|
||||
static const signPage = '/sign_page';
|
||||
static const connectDevices = '/device/connect';
|
||||
static const walletGroupsDisplayPage = '/wallet_groups_display_page';
|
||||
static const walletGroupDescription = '/wallet_group_description';
|
||||
}
|
||||
|
|
|
@ -10,10 +10,18 @@ class CWSolana extends Solana {
|
|||
@override
|
||||
WalletCredentials createSolanaNewWalletCredentials({
|
||||
required String name,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
}) =>
|
||||
SolanaNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
SolanaNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createSolanaRestoreWalletFromSeedCredentials({
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import 'package:another_flushbar/flushbar.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -219,7 +221,7 @@ class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionD
|
|||
widget._authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.newWallet,
|
||||
arguments: widget.walletListViewModel.currentWalletType,
|
||||
arguments: NewWalletArguments(type: widget.walletListViewModel.currentWalletType),
|
||||
conditionToDetermineIfToUse2FA:
|
||||
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/anonpay_transaction_row.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart';
|
||||
import 'package:cake_wallet/themes/extensions/placeholder_theme.dart';
|
||||
|
@ -83,6 +84,19 @@ class TransactionsPage extends StatelessWidget {
|
|||
}
|
||||
|
||||
final transaction = item.transaction;
|
||||
final transactionType = dashboardViewModel.getTransactionType(transaction);
|
||||
|
||||
List<String> tags = [];
|
||||
if (dashboardViewModel.type == WalletType.bitcoin) {
|
||||
if (bitcoin!.txIsReceivedSilentPayment(transaction)) {
|
||||
tags.add(S.of(context).silent_payment);
|
||||
}
|
||||
}
|
||||
if (dashboardViewModel.type == WalletType.litecoin) {
|
||||
if (bitcoin!.txIsMweb(transaction)) {
|
||||
tags.add("MWEB");
|
||||
}
|
||||
}
|
||||
|
||||
return Observer(
|
||||
builder: (_) => TransactionRow(
|
||||
|
@ -96,10 +110,9 @@ class TransactionsPage extends StatelessWidget {
|
|||
? ''
|
||||
: item.formattedFiatAmount,
|
||||
isPending: transaction.isPending,
|
||||
title: item.formattedTitle +
|
||||
item.formattedStatus +
|
||||
' ${item.formattedType}',
|
||||
tag: item.tag,
|
||||
title:
|
||||
item.formattedTitle + item.formattedStatus + transactionType,
|
||||
tags: tags,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ class TransactionRow extends StatelessWidget {
|
|||
required this.formattedAmount,
|
||||
required this.formattedFiatAmount,
|
||||
required this.isPending,
|
||||
required this.tags,
|
||||
required this.title,
|
||||
required this.onTap,
|
||||
required this.tag,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
|
@ -23,7 +23,7 @@ class TransactionRow extends StatelessWidget {
|
|||
final String formattedFiatAmount;
|
||||
final bool isPending;
|
||||
final String title;
|
||||
final String tag;
|
||||
final List<String> tags;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -52,42 +52,22 @@ class TransactionRow extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
if (tag.isNotEmpty)
|
||||
Container(
|
||||
height: 17,
|
||||
padding: EdgeInsets.only(left: 6, right: 6),
|
||||
margin: EdgeInsets.only(right: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.5)),
|
||||
color: Colors.white),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
tag,
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 7,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
formattedAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor),
|
||||
),
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
)),
|
||||
...tags.map((tag) => TxTag(tag: tag)).toList(),
|
||||
],
|
||||
),
|
||||
Text(formattedAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor))
|
||||
]),
|
||||
SizedBox(height: 5),
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
|
||||
|
@ -109,3 +89,32 @@ class TransactionRow extends StatelessWidget {
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
// A tag to add context to a transaction
|
||||
// example use: differ silent payments from regular txs
|
||||
class TxTag extends StatelessWidget {
|
||||
TxTag({required this.tag});
|
||||
|
||||
final String tag;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 17,
|
||||
padding: EdgeInsets.only(left: 6, right: 6),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.5)),
|
||||
color: Theme.of(context).extension<TransactionTradeTheme>()!.rowsColor,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
tag.toLowerCase(),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,12 @@ 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_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.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/themes/extensions/new_wallet_theme.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.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';
|
||||
|
@ -24,6 +26,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
class AdvancedPrivacySettingsPage extends BasePage {
|
||||
AdvancedPrivacySettingsPage({
|
||||
required this.isFromRestore,
|
||||
required this.isChildWallet,
|
||||
required this.useTestnet,
|
||||
required this.toggleUseTestnet,
|
||||
required this.advancedPrivacySettingsViewModel,
|
||||
|
@ -39,25 +42,40 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
|||
String get title => S.current.privacy_settings;
|
||||
|
||||
final bool isFromRestore;
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(isFromRestore, useTestnet,
|
||||
toggleUseTestnet, advancedPrivacySettingsViewModel, nodeViewModel, seedSettingsViewModel);
|
||||
Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(
|
||||
isFromRestore,
|
||||
isChildWallet,
|
||||
useTestnet,
|
||||
toggleUseTestnet,
|
||||
advancedPrivacySettingsViewModel,
|
||||
nodeViewModel,
|
||||
seedSettingsViewModel,
|
||||
);
|
||||
}
|
||||
|
||||
class _AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
const _AdvancedPrivacySettingsBody(this.isFromRestore, this.useTestnet, this.toggleUseTestnet,
|
||||
this.privacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel,
|
||||
{Key? key})
|
||||
: super(key: key);
|
||||
const _AdvancedPrivacySettingsBody(
|
||||
this.isFromRestore,
|
||||
this.isChildWallet,
|
||||
this.useTestnet,
|
||||
this.toggleUseTestnet,
|
||||
this.privacySettingsViewModel,
|
||||
this.nodeViewModel,
|
||||
this.seedTypeViewModel, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final AdvancedPrivacySettingsViewModel privacySettingsViewModel;
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
final SeedSettingsViewModel seedTypeViewModel;
|
||||
|
||||
final bool isFromRestore;
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
|
||||
|
@ -78,6 +96,16 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
|||
|
||||
passphraseController
|
||||
.addListener(() => widget.seedTypeViewModel.setPassphrase(passphraseController.text));
|
||||
|
||||
if (widget.isChildWallet) {
|
||||
if (widget.privacySettingsViewModel.type == WalletType.bitcoin) {
|
||||
widget.seedTypeViewModel.setBitcoinSeedType(BitcoinSeedType.bip39);
|
||||
}
|
||||
|
||||
if (widget.privacySettingsViewModel.type == WalletType.nano) {
|
||||
widget.seedTypeViewModel.setNanoSeedType(NanoSeedType.bip39);
|
||||
}
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -116,7 +144,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
|||
),
|
||||
);
|
||||
}),
|
||||
if (widget.privacySettingsViewModel.hasSeedTypeOption)
|
||||
if (widget.privacySettingsViewModel.isMoneroSeedTypeOptionsEnabled)
|
||||
Observer(builder: (_) {
|
||||
return SettingsChoicesCell(
|
||||
ChoicesListItem<MoneroSeedType>(
|
||||
|
@ -127,15 +155,37 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
|||
),
|
||||
);
|
||||
}),
|
||||
if ([WalletType.bitcoin, WalletType.litecoin]
|
||||
.contains(widget.privacySettingsViewModel.type))
|
||||
if (widget.privacySettingsViewModel.isBitcoinSeedTypeOptionsEnabled)
|
||||
Observer(builder: (_) {
|
||||
return SettingsChoicesCell(
|
||||
ChoicesListItem<BitcoinSeedType>(
|
||||
title: S.current.seedtype,
|
||||
items: BitcoinSeedType.all,
|
||||
selectedItem: widget.seedTypeViewModel.bitcoinSeedType,
|
||||
onItemSelected: widget.seedTypeViewModel.setBitcoinSeedType,
|
||||
onItemSelected: (type) {
|
||||
if (widget.isChildWallet && type != BitcoinSeedType.bip39) {
|
||||
showAlertForSelectingNonBIP39DerivationTypeForChildWallets();
|
||||
} else {
|
||||
widget.seedTypeViewModel.setBitcoinSeedType(type);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (widget.privacySettingsViewModel.isNanoSeedTypeOptionsEnabled)
|
||||
Observer(builder: (_) {
|
||||
return SettingsChoicesCell(
|
||||
ChoicesListItem<NanoSeedType>(
|
||||
title: S.current.seedtype,
|
||||
items: NanoSeedType.all,
|
||||
selectedItem: widget.seedTypeViewModel.nanoSeedType,
|
||||
onItemSelected: (type) {
|
||||
if (widget.isChildWallet && type != NanoSeedType.bip39) {
|
||||
showAlertForSelectingNonBIP39DerivationTypeForChildWallets();
|
||||
} else {
|
||||
widget.seedTypeViewModel.setNanoSeedType(type);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
@ -256,6 +306,19 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
|||
);
|
||||
}
|
||||
|
||||
void showAlertForSelectingNonBIP39DerivationTypeForChildWallets() {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.seedtype_alert_title,
|
||||
alertContent: S.current.seedtype_alert_content,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
passphraseController
|
||||
|
|
|
@ -26,10 +26,15 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class NewWalletPage extends BasePage {
|
||||
NewWalletPage(this._walletNewVM, this._seedSettingsViewModel);
|
||||
NewWalletPage(
|
||||
this._walletNewVM,
|
||||
this._seedSettingsViewModel, {
|
||||
this.isChildWallet = false,
|
||||
});
|
||||
|
||||
final WalletNewVM _walletNewVM;
|
||||
final SeedSettingsViewModel _seedSettingsViewModel;
|
||||
final bool isChildWallet;
|
||||
|
||||
final walletNameImage = Image.asset('assets/images/wallet_name.png');
|
||||
|
||||
|
@ -48,15 +53,23 @@ class NewWalletPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletNameForm(
|
||||
_walletNewVM,
|
||||
currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage,
|
||||
_seedSettingsViewModel);
|
||||
_walletNewVM,
|
||||
currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage,
|
||||
_seedSettingsViewModel,
|
||||
isChildWallet,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletNameForm extends StatefulWidget {
|
||||
WalletNameForm(this._walletNewVM, this.walletImage, this._seedSettingsViewModel);
|
||||
WalletNameForm(
|
||||
this._walletNewVM,
|
||||
this.walletImage,
|
||||
this._seedSettingsViewModel,
|
||||
this.isChildWallet,
|
||||
);
|
||||
|
||||
final WalletNewVM _walletNewVM;
|
||||
final bool isChildWallet;
|
||||
final Image walletImage;
|
||||
final SeedSettingsViewModel _seedSettingsViewModel;
|
||||
|
||||
|
@ -338,7 +351,8 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
|||
Navigator.of(context).pushNamed(Routes.advancedPrivacySettings, arguments: {
|
||||
"type": _walletNewVM.type,
|
||||
"useTestnet": _walletNewVM.useTestnet,
|
||||
"toggleTestnet": _walletNewVM.toggleUseTestnet
|
||||
"toggleTestnet": _walletNewVM.toggleUseTestnet,
|
||||
"isChildWallet": widget.isChildWallet,
|
||||
});
|
||||
},
|
||||
child: Text(S.of(context).advanced_settings),
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/bip39_wallet_utils.dart';
|
||||
import 'package:cake_wallet/routes.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/screens/setup_2fa/widgets/popup_cancellable_alert.dart';
|
||||
|
@ -11,6 +15,7 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.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/new_wallet_type_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
@ -18,21 +23,20 @@ import 'package:flutter/material.dart';
|
|||
|
||||
class NewWalletTypePage extends BasePage {
|
||||
NewWalletTypePage({
|
||||
required this.onTypeSelected,
|
||||
required this.isCreate,
|
||||
required this.isHardwareWallet,
|
||||
required this.newWalletTypeViewModel,
|
||||
required this.newWalletTypeArguments,
|
||||
});
|
||||
|
||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||
final bool isCreate;
|
||||
final bool isHardwareWallet;
|
||||
final NewWalletTypeViewModel newWalletTypeViewModel;
|
||||
final NewWalletTypeArguments newWalletTypeArguments;
|
||||
|
||||
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
||||
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
|
||||
|
||||
@override
|
||||
String get title =>
|
||||
isCreate ? S.current.wallet_list_create_new_wallet : S.current.wallet_list_restore_wallet;
|
||||
String get title => newWalletTypeArguments.isCreate
|
||||
? S.current.wallet_list_create_new_wallet
|
||||
: S.current.wallet_list_restore_wallet;
|
||||
|
||||
@override
|
||||
Function(BuildContext)? get pushToNextWidget => (context) {
|
||||
|
@ -44,24 +48,27 @@ class NewWalletTypePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletTypeForm(
|
||||
onTypeSelected: onTypeSelected,
|
||||
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage,
|
||||
isCreate: isCreate,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
isCreate: newWalletTypeArguments.isCreate,
|
||||
newWalletTypeViewModel: newWalletTypeViewModel,
|
||||
onTypeSelected: newWalletTypeArguments.onTypeSelected,
|
||||
isHardwareWallet: newWalletTypeArguments.isHardwareWallet,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletTypeForm extends StatefulWidget {
|
||||
WalletTypeForm({
|
||||
required this.onTypeSelected,
|
||||
required this.walletImage,
|
||||
required this.isCreate,
|
||||
required this.newWalletTypeViewModel,
|
||||
this.onTypeSelected,
|
||||
required this.isHardwareWallet,
|
||||
});
|
||||
|
||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||
final Image walletImage;
|
||||
final bool isCreate;
|
||||
final Image walletImage;
|
||||
final NewWalletTypeViewModel newWalletTypeViewModel;
|
||||
final void Function(BuildContext, WalletType)? onTypeSelected;
|
||||
final bool isHardwareWallet;
|
||||
|
||||
@override
|
||||
|
@ -179,6 +186,18 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
|||
);
|
||||
}
|
||||
|
||||
widget.onTypeSelected(context, selected!);
|
||||
// If it's a restore flow, trigger the external callback
|
||||
// If it's not a BIP39 Wallet or if there are no other wallets, route to the newWallet page
|
||||
// Any other scenario, route to pre-existing seed page
|
||||
if (!widget.isCreate) {
|
||||
widget.onTypeSelected!(context, selected!);
|
||||
} else if (!isBIP39Wallet(selected!) || !widget.newWalletTypeViewModel.hasExisitingWallet) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: selected!),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(Routes.walletGroupDescription, arguments: selected!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
|
||||
class WalletGroupDescriptionPage extends BasePage {
|
||||
WalletGroupDescriptionPage({required this.selectedWalletType});
|
||||
|
||||
final WalletType selectedWalletType;
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_group;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/wallet_group.png',
|
||||
scale: 0.8,
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: '${S.of(context).wallet_group_description_one} '),
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group.toLowerCase()} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group_description_two} ',
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).choose_wallet_group} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group_description_three} ',
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).create_new_seed} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(text: S.of(context).wallet_group_description_four),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: selectedWalletType),
|
||||
),
|
||||
text: S.of(context).create_new_seed,
|
||||
color: Theme.of(context).cardColor,
|
||||
textColor: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(
|
||||
Routes.walletGroupsDisplayPage,
|
||||
arguments: selectedWalletType,
|
||||
),
|
||||
text: S.of(context).choose_wallet_group,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
192
lib/src/screens/new_wallet/wallet_group_display_page.dart
Normal file
192
lib/src/screens/new_wallet/wallet_group_display_page.dart
Normal file
|
@ -0,0 +1,192 @@
|
|||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
import '../../../themes/extensions/cake_text_theme.dart';
|
||||
|
||||
class WalletGroupsDisplayPage extends BasePage {
|
||||
WalletGroupsDisplayPage(this.walletGroupsDisplayViewModel);
|
||||
|
||||
final WalletGroupsDisplayViewModel walletGroupsDisplayViewModel;
|
||||
|
||||
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
||||
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_group;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletGroupsDisplayBody(
|
||||
walletGroupsDisplayViewModel: walletGroupsDisplayViewModel,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletGroupsDisplayBody extends StatelessWidget {
|
||||
WalletGroupsDisplayBody({required this.walletGroupsDisplayViewModel});
|
||||
|
||||
final WalletGroupsDisplayViewModel walletGroupsDisplayViewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Observer(
|
||||
builder: (context) {
|
||||
return Column(
|
||||
children: [
|
||||
if (walletGroupsDisplayViewModel.hasNoFilteredWallet) ...{
|
||||
WalletGroupEmptyStateWidget(),
|
||||
},
|
||||
...walletGroupsDisplayViewModel.multiWalletGroups.map(
|
||||
(walletGroup) {
|
||||
return Observer(builder: (context) {
|
||||
final index = walletGroupsDisplayViewModel.multiWalletGroups
|
||||
.indexOf(walletGroup);
|
||||
final group = walletGroupsDisplayViewModel.multiWalletGroups[index];
|
||||
final groupName =
|
||||
group.groupName ?? '${S.of(context).wallet_group} ${index + 1}';
|
||||
return GroupedWalletExpansionTile(
|
||||
leadingWidget:
|
||||
Icon(Icons.account_balance_wallet_outlined, size: 28),
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
title: groupName,
|
||||
childWallets: group.wallets.map((walletInfo) {
|
||||
return walletGroupsDisplayViewModel
|
||||
.convertWalletInfoToWalletListItem(walletInfo);
|
||||
}).toList(),
|
||||
isSelected:
|
||||
walletGroupsDisplayViewModel.selectedWalletGroup == group,
|
||||
onTitleTapped: () =>
|
||||
walletGroupsDisplayViewModel.selectWalletGroup(group),
|
||||
onChildItemTapped: (_) =>
|
||||
walletGroupsDisplayViewModel.selectWalletGroup(group),
|
||||
);
|
||||
});
|
||||
},
|
||||
).toList(),
|
||||
...walletGroupsDisplayViewModel.singleWalletsList.map((singleWallet) {
|
||||
return Observer(
|
||||
builder: (context) {
|
||||
final index = walletGroupsDisplayViewModel.singleWalletsList
|
||||
.indexOf(singleWallet);
|
||||
final wallet = walletGroupsDisplayViewModel.singleWalletsList[index];
|
||||
return GroupedWalletExpansionTile(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
title: wallet.name,
|
||||
isSelected:
|
||||
walletGroupsDisplayViewModel.selectedSingleWallet == wallet,
|
||||
leadingWidget: Image.asset(
|
||||
walletTypeToCryptoCurrency(wallet.type).iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
onTitleTapped: () =>
|
||||
walletGroupsDisplayViewModel.selectSingleWallet(wallet),
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return LoadingPrimaryButton(
|
||||
isLoading: walletGroupsDisplayViewModel.isFetchingMnemonic,
|
||||
onPressed: () {
|
||||
if (walletGroupsDisplayViewModel.hasNoFilteredWallet) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: walletGroupsDisplayViewModel.type),
|
||||
);
|
||||
} else {
|
||||
onTypeSelected(context);
|
||||
}
|
||||
},
|
||||
text: walletGroupsDisplayViewModel.hasNoFilteredWallet
|
||||
? S.of(context).create_new_seed
|
||||
: S.of(context).seed_language_next,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !walletGroupsDisplayViewModel.hasNoFilteredWallet
|
||||
? (walletGroupsDisplayViewModel.selectedWalletGroup == null &&
|
||||
walletGroupsDisplayViewModel.selectedSingleWallet == null)
|
||||
: false,
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onTypeSelected(BuildContext context) async {
|
||||
final mnemonic = await walletGroupsDisplayViewModel.getSelectedWalletMnemonic();
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(
|
||||
type: walletGroupsDisplayViewModel.type,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: walletGroupsDisplayViewModel.parentAddress,
|
||||
isChildWallet: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WalletGroupEmptyStateWidget extends StatelessWidget {
|
||||
const WalletGroupEmptyStateWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/wallet_group.png',
|
||||
scale: 0.8,
|
||||
),
|
||||
SizedBox(height: 32),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${S.of(context).wallet_group_empty_state_text_one} ',
|
||||
),
|
||||
TextSpan(
|
||||
text: '${S.of(context).create_new_seed} ',
|
||||
style: TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
TextSpan(text: S.of(context).wallet_group_empty_state_text_two),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
height: 1.5,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GroupedWalletExpansionTile extends StatelessWidget {
|
||||
GroupedWalletExpansionTile({
|
||||
required this.title,
|
||||
required this.isSelected,
|
||||
this.childWallets = const [],
|
||||
this.onTitleTapped,
|
||||
this.onChildItemTapped = _defaultVoidCallback,
|
||||
this.leadingWidget,
|
||||
this.trailingWidget,
|
||||
this.childTrailingWidget,
|
||||
this.decoration,
|
||||
this.color,
|
||||
this.textColor,
|
||||
this.arrowColor,
|
||||
this.borderRadius,
|
||||
this.margin,
|
||||
this.tileKey,
|
||||
}) : super(key: tileKey);
|
||||
|
||||
final Key? tileKey;
|
||||
final bool isSelected;
|
||||
|
||||
final VoidCallback? onTitleTapped;
|
||||
final void Function(WalletListItem item) onChildItemTapped;
|
||||
|
||||
final String title;
|
||||
final Widget? leadingWidget;
|
||||
final Widget? trailingWidget;
|
||||
final Widget Function(WalletListItem)? childTrailingWidget;
|
||||
|
||||
final List<WalletListItem> childWallets;
|
||||
|
||||
final Color? color;
|
||||
final Color? textColor;
|
||||
final Color? arrowColor;
|
||||
final EdgeInsets? margin;
|
||||
final Decoration? decoration;
|
||||
final BorderRadius? borderRadius;
|
||||
|
||||
static void _defaultVoidCallback(WalletListItem ITEM) {}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
|
||||
final effectiveTextColor = textColor ??
|
||||
(isSelected
|
||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
|
||||
|
||||
final effectiveArrowColor = arrowColor ??
|
||||
(isSelected
|
||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: borderRadius ?? BorderRadius.all(Radius.circular(30)),
|
||||
color: backgroundColor,
|
||||
),
|
||||
margin: margin ?? const EdgeInsets.only(bottom: 12.0),
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
dividerColor: Colors.transparent,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
),
|
||||
child: ExpansionTile(
|
||||
key: tileKey,
|
||||
tilePadding: EdgeInsets.symmetric(vertical: 1, horizontal: 16),
|
||||
iconColor: effectiveArrowColor,
|
||||
collapsedIconColor: effectiveArrowColor,
|
||||
leading: leadingWidget,
|
||||
trailing: trailingWidget ?? (childWallets.isEmpty ? SizedBox.shrink() : null),
|
||||
title: GestureDetector(
|
||||
onTap: onTitleTapped,
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: effectiveTextColor,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
children: childWallets.map(
|
||||
(item) {
|
||||
final walletTypeToCrypto = walletTypeToCryptoCurrency(item.type);
|
||||
return ListTile(
|
||||
key: ValueKey(item.name),
|
||||
trailing: childTrailingWidget?.call(item),
|
||||
onTap: () => onChildItemTapped(item),
|
||||
leading: Image.asset(
|
||||
walletTypeToCrypto.iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
title: Text(
|
||||
item.name,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: effectiveTextColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -18,9 +18,11 @@ class SelectButton extends StatelessWidget {
|
|||
this.arrowColor,
|
||||
this.borderColor,
|
||||
this.deviceConnectionTypes,
|
||||
this.borderRadius,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
final Image? image;
|
||||
final Widget? image;
|
||||
final String text;
|
||||
final double textSize;
|
||||
final bool isSelected;
|
||||
|
@ -32,6 +34,8 @@ class SelectButton extends StatelessWidget {
|
|||
final Color? textColor;
|
||||
final Color? arrowColor;
|
||||
final Color? borderColor;
|
||||
final BorderRadius? borderRadius;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -62,10 +66,10 @@ class SelectButton extends StatelessWidget {
|
|||
child: Container(
|
||||
width: double.infinity,
|
||||
height: height,
|
||||
padding: EdgeInsets.only(left: 30, right: 30),
|
||||
padding: padding ?? EdgeInsets.only(left: 30, right: 30),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
borderRadius: borderRadius ?? BorderRadius.all(Radius.circular(30)),
|
||||
color: backgroundColor,
|
||||
border: borderColor != null ? Border.all(color: borderColor!) : null,
|
||||
),
|
||||
|
|
|
@ -39,6 +39,7 @@ class RescanPage extends BasePage {
|
|||
toggleSingleScan: () =>
|
||||
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
|
||||
walletType: _rescanViewModel.wallet.type,
|
||||
bitcoinMempoolAPIEnabled: _rescanViewModel.isBitcoinMempoolAPIEnabled,
|
||||
)),
|
||||
Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
|
|
|
@ -281,16 +281,9 @@ class WalletRestorePage extends BasePage {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((walletRestoreViewModel.type == WalletType.litecoin) &&
|
||||
(seedWords.length != WalletRestoreViewModelBase.electrumSeedMnemonicLength &&
|
||||
seedWords.length != WalletRestoreViewModelBase.electrumShortSeedMnemonicLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// bip39:
|
||||
const validSeedLengths = [12, 18, 24];
|
||||
if (walletRestoreViewModel.type == WalletType.bitcoin &&
|
||||
!(validSeedLengths.contains(seedWords.length))) {
|
||||
if (!(validSeedLengths.contains(seedWords.length))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,11 +68,11 @@ class SendPage extends BasePage {
|
|||
|
||||
@override
|
||||
Function(BuildContext)? get pushToNextWidget => (context) {
|
||||
FocusScopeNode currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus) {
|
||||
currentFocus.focusedChild?.unfocus();
|
||||
}
|
||||
};
|
||||
FocusScopeNode currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus) {
|
||||
currentFocus.focusedChild?.unfocus();
|
||||
}
|
||||
};
|
||||
|
||||
@override
|
||||
Widget? leading(BuildContext context) {
|
||||
|
@ -212,26 +212,25 @@ class SendPage extends BasePage {
|
|||
final count = sendViewModel.outputs.length;
|
||||
|
||||
return count > 1
|
||||
? Semantics (
|
||||
label: 'Page Indicator',
|
||||
hint: 'Swipe to change receiver',
|
||||
excludeSemantics: true,
|
||||
child:
|
||||
SmoothPageIndicator(
|
||||
controller: controller,
|
||||
count: count,
|
||||
effect: ScrollingDotsEffect(
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context)
|
||||
.extension<SendPageTheme>()!
|
||||
.indicatorDotColor,
|
||||
activeDotColor: Theme.of(context)
|
||||
.extension<SendPageTheme>()!
|
||||
.templateBackgroundColor),
|
||||
))
|
||||
? Semantics(
|
||||
label: 'Page Indicator',
|
||||
hint: 'Swipe to change receiver',
|
||||
excludeSemantics: true,
|
||||
child: SmoothPageIndicator(
|
||||
controller: controller,
|
||||
count: count,
|
||||
effect: ScrollingDotsEffect(
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context)
|
||||
.extension<SendPageTheme>()!
|
||||
.indicatorDotColor,
|
||||
activeDotColor: Theme.of(context)
|
||||
.extension<SendPageTheme>()!
|
||||
.templateBackgroundColor),
|
||||
))
|
||||
: Offstage();
|
||||
},
|
||||
),
|
||||
|
@ -478,6 +477,7 @@ class SendPage extends BasePage {
|
|||
feeValue: sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: sendViewModel.outputs,
|
||||
change: sendViewModel.pendingTransaction!.change,
|
||||
rightButtonText: S.of(_dialogContext).send,
|
||||
leftButtonText: S.of(_dialogContext).cancel,
|
||||
actionRightButton: () async {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -21,6 +22,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
required this.feeValue,
|
||||
required this.feeFiatAmount,
|
||||
required this.outputs,
|
||||
this.change,
|
||||
required this.leftButtonText,
|
||||
required this.rightButtonText,
|
||||
required this.actionLeftButton,
|
||||
|
@ -44,6 +46,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
final String feeValue;
|
||||
final String feeFiatAmount;
|
||||
final List<Output> outputs;
|
||||
final PendingChange? change;
|
||||
final String leftButtonText;
|
||||
final String rightButtonText;
|
||||
final VoidCallback actionLeftButton;
|
||||
|
@ -101,6 +104,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
feeValue: feeValue,
|
||||
feeFiatAmount: feeFiatAmount,
|
||||
outputs: outputs,
|
||||
change: change,
|
||||
onDispose: onDispose);
|
||||
}
|
||||
|
||||
|
@ -117,6 +121,7 @@ class ConfirmSendingAlertContent extends StatefulWidget {
|
|||
required this.feeValue,
|
||||
required this.feeFiatAmount,
|
||||
required this.outputs,
|
||||
this.change,
|
||||
required this.onDispose}) {}
|
||||
|
||||
final String? paymentId;
|
||||
|
@ -130,6 +135,7 @@ class ConfirmSendingAlertContent extends StatefulWidget {
|
|||
final String feeValue;
|
||||
final String feeFiatAmount;
|
||||
final List<Output> outputs;
|
||||
final PendingChange? change;
|
||||
final Function? onDispose;
|
||||
|
||||
@override
|
||||
|
@ -145,6 +151,7 @@ class ConfirmSendingAlertContent extends StatefulWidget {
|
|||
feeValue: feeValue,
|
||||
feeFiatAmount: feeFiatAmount,
|
||||
outputs: outputs,
|
||||
change: change,
|
||||
onDispose: onDispose);
|
||||
}
|
||||
|
||||
|
@ -161,6 +168,7 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
|
|||
required this.feeValue,
|
||||
required this.feeFiatAmount,
|
||||
required this.outputs,
|
||||
this.change,
|
||||
this.onDispose})
|
||||
: recipientTitle = '' {
|
||||
recipientTitle = outputs.length > 1
|
||||
|
@ -179,6 +187,7 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
|
|||
final String feeValue;
|
||||
final String feeFiatAmount;
|
||||
final List<Output> outputs;
|
||||
final PendingChange? change;
|
||||
final Function? onDispose;
|
||||
|
||||
final double backgroundHeight = 160;
|
||||
|
@ -391,100 +400,57 @@ class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent>
|
|||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
outputs.length > 1
|
||||
? ListView.builder(
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: outputs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = outputs[index];
|
||||
final _address =
|
||||
item.isParsedAddress ? item.extractedAddress : item.address;
|
||||
final _amount = item.cryptoAmount.replaceAll(',', '.');
|
||||
ListView.builder(
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: outputs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = outputs[index];
|
||||
final _address =
|
||||
item.isParsedAddress ? item.extractedAddress : item.address;
|
||||
final _amount = item.cryptoAmount.replaceAll(',', '.');
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
if (item.isParsedAddress)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
item.parsedAddress.name,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Lato',
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
_address,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Lato',
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
_amount,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Lato',
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)
|
||||
],
|
||||
))
|
||||
],
|
||||
);
|
||||
})
|
||||
: Column(children: [
|
||||
if (outputs.first.isParsedAddress)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
outputs.first.parsedAddress.name,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Lato',
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
outputs.first.isParsedAddress
|
||||
? outputs.first.extractedAddress
|
||||
: outputs.first.address,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Lato',
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
])
|
||||
return Column(
|
||||
children: [
|
||||
if (item.isParsedAddress)
|
||||
AddressText(text: item.parsedAddress.name),
|
||||
AddressText(text: _address, fontSize: 10),
|
||||
if (stealthAddressText(item.stealthAddress) != null)
|
||||
AddressText(
|
||||
text: stealthAddressText(item.stealthAddress)!, fontSize: 10),
|
||||
AmountText(text: _amount),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
if (change != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).send_change_to_you,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: 'Lato',
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
AddressText(text: change!.address, fontSize: 10),
|
||||
AmountText(text: change!.amount),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
))),
|
||||
if (showScrollbar)
|
||||
|
@ -539,3 +505,78 @@ class ExpirationTimeWidget extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddressText extends StatelessWidget {
|
||||
final String text;
|
||||
final double fontSize;
|
||||
final FontWeight fontWeight;
|
||||
final TextAlign? textAlign;
|
||||
|
||||
const AddressText({
|
||||
required this.text,
|
||||
this.fontSize = 14,
|
||||
this.fontWeight = FontWeight.w600,
|
||||
this.textAlign,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: 'Lato',
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AmountText extends StatelessWidget {
|
||||
final String text;
|
||||
final double fontSize;
|
||||
final FontWeight fontWeight;
|
||||
final TextAlign? textAlign;
|
||||
|
||||
const AmountText({
|
||||
required this.text,
|
||||
this.fontSize = 10,
|
||||
this.fontWeight = FontWeight.w600,
|
||||
this.textAlign,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: 'Lato',
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
String? stealthAddressText(String? stealthAddress) {
|
||||
if (stealthAddress == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return stealthAddress.isNotEmpty ? "-> $stealthAddress" : null;
|
||||
}
|
||||
|
|
|
@ -58,8 +58,8 @@ class PrivacyPage extends BasePage {
|
|||
if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible)
|
||||
SettingsSwitcherCell(
|
||||
title: _privacySettingsViewModel.isMoneroWallet
|
||||
? S.current.auto_generate_subaddresses
|
||||
: S.current.auto_generate_addresses,
|
||||
? S.current.auto_generate_subaddresses
|
||||
: S.current.auto_generate_addresses,
|
||||
value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setAutoGenerateSubaddresses(value);
|
||||
|
@ -115,21 +115,21 @@ class PrivacyPage extends BasePage {
|
|||
),
|
||||
if (_privacySettingsViewModel.canUseMempoolFeeAPI)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.live_fee_rates,
|
||||
title: S.current.enable_mempool_api,
|
||||
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));
|
||||
}) ??
|
||||
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);
|
||||
|
|
|
@ -24,19 +24,22 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class RBFDetailsPage extends BasePage {
|
||||
RBFDetailsPage({required this.transactionDetailsViewModel});
|
||||
RBFDetailsPage({required this.transactionDetailsViewModel, required this.rawTransaction}) {
|
||||
transactionDetailsViewModel.addBumpFeesListItems(
|
||||
transactionDetailsViewModel.transactionInfo, rawTransaction);
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => S.current.bump_fee;
|
||||
|
||||
final TransactionDetailsViewModel transactionDetailsViewModel;
|
||||
final String rawTransaction;
|
||||
|
||||
bool _effectsInstalled = false;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
_setEffects(context);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
|
@ -166,7 +169,9 @@ class RBFDetailsPage extends BasePage {
|
|||
actionRightButton: () async {
|
||||
Navigator.of(popupContext).pop();
|
||||
await transactionDetailsViewModel.sendViewModel.commitTransaction();
|
||||
// transactionStatePopup();
|
||||
try {
|
||||
Navigator.of(popupContext).pop();
|
||||
} catch (_) {}
|
||||
},
|
||||
actionLeftButton: () => Navigator.of(popupContext).pop(),
|
||||
feeFiatAmount:
|
||||
|
|
|
@ -75,7 +75,7 @@ class TransactionDetailsPage extends BasePage {
|
|||
text: S.of(context).bump_fee,
|
||||
onTap: () async {
|
||||
Navigator.of(context).pushNamed(Routes.bumpFeePage,
|
||||
arguments: transactionDetailsViewModel.transactionInfo);
|
||||
arguments: [transactionDetailsViewModel.transactionInfo, transactionDetailsViewModel.rawTransaction]);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
import 'package:another_flushbar/flushbar.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
|
@ -22,29 +18,29 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
|||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
|
||||
class WalletEditPage extends BasePage {
|
||||
WalletEditPage(
|
||||
{required this.walletEditViewModel,
|
||||
required this.editingWallet,
|
||||
required this.walletNewVM,
|
||||
required this.authService})
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
WalletEditPage({
|
||||
required this.pageArguments,
|
||||
}) : _formKey = GlobalKey<FormState>(),
|
||||
_labelController = TextEditingController(),
|
||||
walletEditViewModel = pageArguments.walletEditViewModel!,
|
||||
super() {
|
||||
_labelController.text = editingWallet.name;
|
||||
_labelController.text =
|
||||
pageArguments.isWalletGroup ? pageArguments.groupName : pageArguments.editingWallet.name;
|
||||
_labelController.addListener(() => walletEditViewModel.newName = _labelController.text);
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _labelController;
|
||||
|
||||
final WalletEditPageArguments pageArguments;
|
||||
final WalletEditViewModel walletEditViewModel;
|
||||
final WalletNewVM walletNewVM;
|
||||
final WalletListItem editingWallet;
|
||||
final AuthService authService;
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_list_edit_wallet;
|
||||
String get title => pageArguments.isWalletGroup
|
||||
? S.current.wallet_list_edit_group_name
|
||||
: S.current.wallet_list_edit_wallet;
|
||||
|
||||
Flushbar<void>? _progressBar;
|
||||
|
||||
|
@ -57,11 +53,14 @@ class WalletEditPage extends BasePage {
|
|||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _labelController,
|
||||
hintText: S.of(context).wallet_list_wallet_name,
|
||||
validator: WalletNameValidator()))),
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _labelController,
|
||||
hintText: S.of(context).wallet_list_wallet_name,
|
||||
validator: WalletNameValidator(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
final isLoading = walletEditViewModel.state is WalletEditRenamePending ||
|
||||
|
@ -69,24 +68,26 @@ class WalletEditPage extends BasePage {
|
|||
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
isDisabled: isLoading,
|
||||
onPressed: () => _removeWallet(context),
|
||||
text: S.of(context).delete,
|
||||
color: Palette.red,
|
||||
textColor: Colors.white),
|
||||
if (!pageArguments.isWalletGroup)
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
isDisabled: isLoading,
|
||||
onPressed: () => _removeWallet(context),
|
||||
text: S.of(context).delete,
|
||||
color: Palette.red,
|
||||
textColor: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
if (walletNewVM.nameExists(walletEditViewModel.newName)) {
|
||||
if (pageArguments.walletNewVM!
|
||||
.nameExists(walletEditViewModel.newName)) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
|
@ -102,29 +103,33 @@ class WalletEditPage extends BasePage {
|
|||
try {
|
||||
bool confirmed = false;
|
||||
|
||||
if (SettingsStoreBase
|
||||
.walletPasswordDirectInput) {
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
await Navigator.of(context).pushNamed(
|
||||
Routes.walletUnlockLoadable,
|
||||
arguments: WalletUnlockArguments(
|
||||
authPasswordHandler:
|
||||
(String password) async {
|
||||
await walletEditViewModel
|
||||
.changeName(editingWallet,
|
||||
password: password);
|
||||
authPasswordHandler: (String password) async {
|
||||
await walletEditViewModel.changeName(
|
||||
pageArguments.editingWallet,
|
||||
password: password,
|
||||
isWalletGroup: pageArguments.isWalletGroup,
|
||||
groupParentAddress: pageArguments.parentAddress,
|
||||
);
|
||||
},
|
||||
callback: (bool
|
||||
isAuthenticatedSuccessfully,
|
||||
callback: (bool isAuthenticatedSuccessfully,
|
||||
AuthPageState auth) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close();
|
||||
confirmed = true;
|
||||
}
|
||||
},
|
||||
walletName: editingWallet.name,
|
||||
walletType: editingWallet.type));
|
||||
walletName: pageArguments.editingWallet.name,
|
||||
walletType: pageArguments.editingWallet.type));
|
||||
} else {
|
||||
await walletEditViewModel.changeName(editingWallet);
|
||||
await walletEditViewModel.changeName(
|
||||
pageArguments.editingWallet,
|
||||
isWalletGroup: pageArguments.isWalletGroup,
|
||||
groupParentAddress: pageArguments.parentAddress,
|
||||
);
|
||||
confirmed = true;
|
||||
}
|
||||
|
||||
|
@ -154,7 +159,9 @@ class WalletEditPage extends BasePage {
|
|||
}
|
||||
|
||||
Future<void> _removeWallet(BuildContext context) async {
|
||||
authService.authenticateAction(context, onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
pageArguments.authService!.authenticateAction(
|
||||
context,
|
||||
onAuthSuccess: (isAuthenticatedSuccessfully) async {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
@ -173,7 +180,8 @@ class WalletEditPage extends BasePage {
|
|||
builder: (BuildContext dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).delete_wallet,
|
||||
alertContent: S.of(context).delete_wallet_confirm_message(editingWallet.name),
|
||||
alertContent:
|
||||
S.of(context).delete_wallet_confirm_message(pageArguments.editingWallet.name),
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).delete,
|
||||
actionLeftButton: () => Navigator.of(dialogContext).pop(),
|
||||
|
@ -187,13 +195,16 @@ class WalletEditPage extends BasePage {
|
|||
Navigator.of(context).pop();
|
||||
|
||||
try {
|
||||
changeProcessText(context, S.of(context).wallet_list_removing_wallet(editingWallet.name));
|
||||
await walletEditViewModel.remove(editingWallet);
|
||||
changeProcessText(
|
||||
context, S.of(context).wallet_list_removing_wallet(pageArguments.editingWallet.name));
|
||||
await walletEditViewModel.remove(pageArguments.editingWallet);
|
||||
hideProgressText();
|
||||
} catch (e) {
|
||||
changeProcessText(
|
||||
context,
|
||||
S.of(context).wallet_list_failed_to_remove(editingWallet.name, e.toString()),
|
||||
S
|
||||
.of(context)
|
||||
.wallet_list_failed_to_remove(pageArguments.editingWallet.name, e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
54
lib/src/screens/wallet_list/edit_wallet_button_widget.dart
Normal file
54
lib/src/screens/wallet_list/edit_wallet_button_widget.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EditWalletButtonWidget extends StatelessWidget {
|
||||
const EditWalletButtonWidget({
|
||||
required this.width,
|
||||
required this.onTap,
|
||||
this.isGroup = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final bool isGroup;
|
||||
final double width;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: width,
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: 40,
|
||||
width: 44,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
size: 14,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isGroup) ...{
|
||||
SizedBox(width: 6),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
size: 24,
|
||||
color: Theme.of(context).extension<FilterTheme>()!.titlesColor,
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,11 +7,13 @@ class FilteredList extends StatefulWidget {
|
|||
required this.list,
|
||||
required this.itemBuilder,
|
||||
required this.updateFunction,
|
||||
this.shrinkWrap = false,
|
||||
});
|
||||
|
||||
final ObservableList<dynamic> list;
|
||||
final Widget Function(BuildContext, int) itemBuilder;
|
||||
final Function updateFunction;
|
||||
final bool shrinkWrap;
|
||||
|
||||
@override
|
||||
FilteredListState createState() => FilteredListState();
|
||||
|
@ -22,6 +24,7 @@ class FilteredListState extends State<FilteredList> {
|
|||
Widget build(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_) => ReorderableListView.builder(
|
||||
shrinkWrap: widget.shrinkWrap,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemBuilder: widget.itemBuilder,
|
||||
itemCount: widget.list.length,
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_edit_page_arguments.dart';
|
||||
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/edit_wallet_button_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/filtered_list.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
@ -7,8 +11,6 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
|||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
|
@ -23,7 +25,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
|||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
||||
|
||||
class WalletListPage extends BasePage {
|
||||
WalletListPage({required this.walletListViewModel, required this.authService});
|
||||
|
@ -128,112 +129,143 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
list: widget.walletListViewModel.wallets,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (__, index) {
|
||||
final wallet = widget.walletListViewModel.wallets[index];
|
||||
final currentColor = wallet.isCurrent
|
||||
? Theme.of(context)
|
||||
.extension<WalletListTheme>()!
|
||||
.createNewWalletButtonBackgroundColor
|
||||
: Theme.of(context).colorScheme.background;
|
||||
final row = GestureDetector(
|
||||
key: ValueKey(wallet.name),
|
||||
onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
child: Container(
|
||||
height: tileHeight,
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: tileHeight,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(4),
|
||||
bottomRight: Radius.circular(4)),
|
||||
color: currentColor),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: tileHeight,
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
wallet.isEnabled
|
||||
? _imageFor(
|
||||
type: wallet.type,
|
||||
isTestnet: wallet.isTestnet,
|
||||
)
|
||||
: nonWalletTypeIcon,
|
||||
SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: Text(
|
||||
wallet.name,
|
||||
maxLines: null,
|
||||
softWrap: true,
|
||||
style: TextStyle(
|
||||
fontSize: DeviceInfo.instance.isDesktop ? 18 : 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<CakeTextTheme>()!
|
||||
.titleColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.walletListViewModel.multiWalletGroups.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.shared_seed_wallet_groups,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return wallet.isCurrent
|
||||
? row
|
||||
: Row(
|
||||
key: ValueKey(wallet.name),
|
||||
children: [
|
||||
Expanded(child: row),
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.walletEdit,
|
||||
arguments: [widget.walletListViewModel, wallet]),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
right: DeviceInfo.instance.isMobile ? 20 : 40),
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: 40,
|
||||
width: 44,
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.iconsBackgroundColor,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.edit,
|
||||
size: 14,
|
||||
color: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.iconsColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.multiWalletGroups,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final group = widget.walletListViewModel.multiWalletGroups[index];
|
||||
final groupName = group.groupName ??
|
||||
'${S.current.wallet_group} ${index + 1}';
|
||||
return GroupedWalletExpansionTile(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
margin: EdgeInsets.only(left: 20, right: 20, bottom: 12),
|
||||
title: groupName,
|
||||
tileKey: ValueKey('group_wallets_expansion_tile_widget_$index'),
|
||||
leadingWidget: Icon(
|
||||
Icons.account_balance_wallet_outlined,
|
||||
size: 28,
|
||||
),
|
||||
],
|
||||
);
|
||||
trailingWidget: EditWalletButtonWidget(
|
||||
width: 74,
|
||||
isGroup: true,
|
||||
onTap: () {
|
||||
final wallet = widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(group.wallets.first);
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
isWalletGroup: true,
|
||||
groupName: groupName,
|
||||
parentAddress: group.parentAddress,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
childWallets: group.wallets.map((walletInfo) {
|
||||
return widget.walletListViewModel
|
||||
.convertWalletInfoToWalletListItem(walletInfo);
|
||||
}).toList(),
|
||||
isSelected: false,
|
||||
onChildItemTapped: (wallet) =>
|
||||
wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
childTrailingWidget: (item) {
|
||||
return item.isCurrent
|
||||
? SizedBox.shrink()
|
||||
: EditWalletButtonWidget(
|
||||
width: 44,
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: item,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
},
|
||||
),
|
||||
if (widget.walletListViewModel.singleWalletsList.isNotEmpty) ...{
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 24),
|
||||
child: Text(
|
||||
S.current.single_seed_wallets_group,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
child: Observer(
|
||||
builder: (_) => FilteredList(
|
||||
shrinkWrap: true,
|
||||
list: widget.walletListViewModel.singleWalletsList,
|
||||
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
|
||||
itemBuilder: (context, index) {
|
||||
final wallet = widget.walletListViewModel.singleWalletsList[index];
|
||||
|
||||
return GroupedWalletExpansionTile(
|
||||
tileKey: ValueKey('single_wallets_expansion_tile_widget_$index'),
|
||||
leadingWidget: Image.asset(
|
||||
walletTypeToCryptoCurrency(wallet.type).iconPath!,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
title: wallet.name,
|
||||
isSelected: false,
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
margin: EdgeInsets.only(left: 20, right: 20, bottom: 12),
|
||||
onTitleTapped: () => wallet.isCurrent ? null : _loadWallet(wallet),
|
||||
trailingWidget: wallet.isCurrent
|
||||
? null
|
||||
: EditWalletButtonWidget(
|
||||
width: 44,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletEdit,
|
||||
arguments: WalletEditPageArguments(
|
||||
walletListViewModel: widget.walletListViewModel,
|
||||
editingWallet: wallet,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -249,14 +281,18 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
widget.authService.authenticateAction(
|
||||
context,
|
||||
route: Routes.newWallet,
|
||||
arguments: widget.walletListViewModel.currentWalletType,
|
||||
arguments: NewWalletArguments(
|
||||
type: widget.walletListViewModel.currentWalletType,
|
||||
),
|
||||
conditionToDetermineIfToUse2FA:
|
||||
widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: widget.walletListViewModel.currentWalletType,
|
||||
arguments: NewWalletArguments(
|
||||
type: widget.walletListViewModel.currentWalletType,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -340,15 +376,15 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
|
||||
Future<void> _loadWallet(WalletListItem wallet) async {
|
||||
if (SettingsStoreBase.walletPasswordDirectInput) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.walletUnlockLoadable,
|
||||
Navigator.of(context).pushNamed(Routes.walletUnlockLoadable,
|
||||
arguments: WalletUnlockArguments(
|
||||
callback: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
|
||||
if (isAuthenticatedSuccessfully) {
|
||||
auth.close();
|
||||
setState(() {});
|
||||
}
|
||||
}, walletName: wallet.name,
|
||||
},
|
||||
walletName: wallet.name,
|
||||
walletType: wallet.type));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ class BlockchainHeightWidget extends StatefulWidget {
|
|||
this.isMwebScan = false,
|
||||
this.toggleSingleScan,
|
||||
this.doSingleScan = false,
|
||||
this.bitcoinMempoolAPIEnabled,
|
||||
required this.walletType,
|
||||
}) : super(key: key);
|
||||
|
||||
|
@ -31,6 +32,7 @@ class BlockchainHeightWidget extends StatefulWidget {
|
|||
final bool isSilentPaymentsScan;
|
||||
final bool isMwebScan;
|
||||
final bool doSingleScan;
|
||||
final Future<bool>? bitcoinMempoolAPIEnabled;
|
||||
final Function()? toggleSingleScan;
|
||||
final WalletType walletType;
|
||||
|
||||
|
@ -81,7 +83,8 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
|||
child: BaseTextFormField(
|
||||
focusNode: widget.focusNode,
|
||||
controller: restoreHeightController,
|
||||
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: false),
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(signed: false, decimal: false),
|
||||
hintText: widget.isSilentPaymentsScan
|
||||
? S.of(context).silent_payments_scan_from_height
|
||||
: S.of(context).widgets_restore_from_blockheight,
|
||||
|
@ -148,7 +151,9 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
|||
: S.of(context).restore_from_date_or_blockheight,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12, fontWeight: FontWeight.normal, color: Theme.of(context).hintColor),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context).hintColor),
|
||||
),
|
||||
)
|
||||
]
|
||||
|
@ -170,7 +175,10 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
|||
if (widget.isMwebScan) {
|
||||
height = bitcoin!.getLitecoinHeightByDate(date: date);
|
||||
} else if (widget.isSilentPaymentsScan) {
|
||||
height = bitcoin!.getHeightByDate(date: date);
|
||||
height = await bitcoin!.getHeightByDate(
|
||||
date: date,
|
||||
bitcoinMempoolAPIEnabled: await widget.bitcoinMempoolAPIEnabled,
|
||||
);
|
||||
} else {
|
||||
if (widget.walletType == WalletType.monero) {
|
||||
height = monero!.getHeightByDate(date: date);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -6,12 +7,13 @@ import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
|
|||
|
||||
part 'transaction_filter_store.g.dart';
|
||||
|
||||
class TransactionFilterStore = TransactionFilterStoreBase
|
||||
with _$TransactionFilterStore;
|
||||
class TransactionFilterStore = TransactionFilterStoreBase with _$TransactionFilterStore;
|
||||
|
||||
abstract class TransactionFilterStoreBase with Store {
|
||||
TransactionFilterStoreBase() : displayIncoming = true,
|
||||
displayOutgoing = true;
|
||||
TransactionFilterStoreBase()
|
||||
: displayIncoming = true,
|
||||
displayOutgoing = true,
|
||||
displaySilentPayments = true;
|
||||
|
||||
@observable
|
||||
bool displayIncoming;
|
||||
|
@ -19,6 +21,9 @@ abstract class TransactionFilterStoreBase with Store {
|
|||
@observable
|
||||
bool displayOutgoing;
|
||||
|
||||
@observable
|
||||
bool displaySilentPayments;
|
||||
|
||||
@observable
|
||||
DateTime? startDate;
|
||||
|
||||
|
@ -26,31 +31,36 @@ abstract class TransactionFilterStoreBase with Store {
|
|||
DateTime? endDate;
|
||||
|
||||
@computed
|
||||
bool get displayAll => displayIncoming && displayOutgoing;
|
||||
bool get displayAll => displayIncoming && displayOutgoing && displaySilentPayments;
|
||||
|
||||
@action
|
||||
void toggleAll() {
|
||||
if (displayAll) {
|
||||
displayOutgoing = false;
|
||||
displayIncoming = false;
|
||||
displaySilentPayments = false;
|
||||
} else {
|
||||
displayOutgoing = true;
|
||||
displayIncoming = true;
|
||||
displaySilentPayments = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@action
|
||||
void toggleIncoming() {
|
||||
displayIncoming = !displayIncoming;
|
||||
}
|
||||
|
||||
|
||||
@action
|
||||
void toggleOutgoing() {
|
||||
displayOutgoing = !displayOutgoing;
|
||||
}
|
||||
|
||||
@action
|
||||
void toggleSilentPayments() {
|
||||
displaySilentPayments = !displaySilentPayments;
|
||||
}
|
||||
|
||||
@action
|
||||
void changeStartDate(DateTime date) => startDate = date;
|
||||
|
||||
|
@ -59,34 +69,33 @@ abstract class TransactionFilterStoreBase with Store {
|
|||
|
||||
List<ActionListItem> filtered({required List<ActionListItem> transactions}) {
|
||||
var _transactions = <ActionListItem>[];
|
||||
final needToFilter = !displayAll ||
|
||||
(startDate != null && endDate != null);
|
||||
final needToFilter = !displayAll || (startDate != null && endDate != null);
|
||||
|
||||
if (needToFilter) {
|
||||
_transactions = transactions.where((item) {
|
||||
var allowed = true;
|
||||
|
||||
if (allowed && startDate != null && endDate != null) {
|
||||
if(item is TransactionListItem){
|
||||
allowed = (startDate?.isBefore(item.transaction.date) ?? false)
|
||||
&& (endDate?.isAfter(item.transaction.date) ?? false);
|
||||
}else if(item is AnonpayTransactionListItem){
|
||||
allowed = (startDate?.isBefore(item.transaction.createdAt) ?? false)
|
||||
&& (endDate?.isAfter(item.transaction.createdAt) ?? false);
|
||||
}
|
||||
if (item is TransactionListItem) {
|
||||
allowed = (startDate?.isBefore(item.transaction.date) ?? false) &&
|
||||
(endDate?.isAfter(item.transaction.date) ?? false);
|
||||
} else if (item is AnonpayTransactionListItem) {
|
||||
allowed = (startDate?.isBefore(item.transaction.createdAt) ?? false) &&
|
||||
(endDate?.isAfter(item.transaction.createdAt) ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowed && (!displayAll)) {
|
||||
if(item is TransactionListItem){
|
||||
allowed = (displayOutgoing &&
|
||||
item.transaction.direction ==
|
||||
TransactionDirection.outgoing) ||
|
||||
(displayIncoming &&
|
||||
item.transaction.direction == TransactionDirection.incoming);
|
||||
} else if(item is AnonpayTransactionListItem){
|
||||
if (item is TransactionListItem) {
|
||||
allowed =
|
||||
(displayOutgoing && item.transaction.direction == TransactionDirection.outgoing) ||
|
||||
(displayIncoming &&
|
||||
item.transaction.direction == TransactionDirection.incoming &&
|
||||
!bitcoin!.txIsReceivedSilentPayment(item.transaction)) ||
|
||||
(displaySilentPayments && bitcoin!.txIsReceivedSilentPayment(item.transaction));
|
||||
} else if (item is AnonpayTransactionListItem) {
|
||||
allowed = displayIncoming;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return allowed;
|
||||
|
@ -97,4 +106,4 @@ abstract class TransactionFilterStoreBase with Store {
|
|||
|
||||
return _transactions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ abstract class SettingsStoreBase with Store {
|
|||
required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus,
|
||||
required MoneroSeedType initialMoneroSeedType,
|
||||
required BitcoinSeedType initialBitcoinSeedType,
|
||||
required NanoSeedType initialNanoSeedType,
|
||||
required bool initialAppSecure,
|
||||
required bool initialDisableBuy,
|
||||
required bool initialDisableSell,
|
||||
|
@ -137,6 +138,7 @@ abstract class SettingsStoreBase with Store {
|
|||
autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus,
|
||||
moneroSeedType = initialMoneroSeedType,
|
||||
bitcoinSeedType = initialBitcoinSeedType,
|
||||
nanoSeedType = initialNanoSeedType,
|
||||
fiatApiMode = initialFiatMode,
|
||||
allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
|
||||
selectedCake2FAPreset = initialCake2FAPresetOptions,
|
||||
|
@ -347,6 +349,11 @@ abstract class SettingsStoreBase with Store {
|
|||
(BitcoinSeedType bitcoinSeedType) => sharedPreferences.setInt(
|
||||
PreferencesKey.bitcoinSeedType, bitcoinSeedType.raw));
|
||||
|
||||
reaction(
|
||||
(_) => nanoSeedType,
|
||||
(NanoSeedType nanoSeedType) =>
|
||||
sharedPreferences.setInt(PreferencesKey.nanoSeedType, nanoSeedType.raw));
|
||||
|
||||
reaction(
|
||||
(_) => fiatApiMode,
|
||||
(FiatApiMode mode) =>
|
||||
|
@ -597,6 +604,7 @@ abstract class SettingsStoreBase with Store {
|
|||
static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords;
|
||||
static const defaultMoneroSeedType = MoneroSeedType.defaultSeedType;
|
||||
static const defaultBitcoinSeedType = BitcoinSeedType.defaultDerivationType;
|
||||
static const defaultNanoSeedType = NanoSeedType.defaultDerivationType;
|
||||
|
||||
@observable
|
||||
FiatCurrency fiatCurrency;
|
||||
|
@ -631,6 +639,9 @@ abstract class SettingsStoreBase with Store {
|
|||
@observable
|
||||
BitcoinSeedType bitcoinSeedType;
|
||||
|
||||
@observable
|
||||
NanoSeedType nanoSeedType;
|
||||
|
||||
@observable
|
||||
bool isAppSecure;
|
||||
|
||||
|
@ -1022,6 +1033,11 @@ abstract class SettingsStoreBase with Store {
|
|||
? BitcoinSeedType.deserialize(raw: _bitcoinSeedType)
|
||||
: defaultBitcoinSeedType;
|
||||
|
||||
final _nanoSeedType = sharedPreferences.getInt(PreferencesKey.nanoSeedType);
|
||||
|
||||
final nanoSeedType =
|
||||
_nanoSeedType != null ? NanoSeedType.deserialize(raw: _nanoSeedType) : defaultNanoSeedType;
|
||||
|
||||
final nodes = <WalletType, Node>{};
|
||||
final powNodes = <WalletType, Node>{};
|
||||
|
||||
|
@ -1187,6 +1203,7 @@ abstract class SettingsStoreBase with Store {
|
|||
initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus,
|
||||
initialMoneroSeedType: moneroSeedType,
|
||||
initialBitcoinSeedType: bitcoinSeedType,
|
||||
initialNanoSeedType: nanoSeedType,
|
||||
initialAppSecure: isAppSecure,
|
||||
initialDisableBuy: disableBuy,
|
||||
initialDisableSell: disableSell,
|
||||
|
@ -1324,6 +1341,11 @@ abstract class SettingsStoreBase with Store {
|
|||
? BitcoinSeedType.deserialize(raw: _bitcoinSeedType)
|
||||
: defaultBitcoinSeedType;
|
||||
|
||||
final _nanoSeedType = sharedPreferences.getInt(PreferencesKey.nanoSeedType);
|
||||
|
||||
nanoSeedType =
|
||||
_nanoSeedType != null ? NanoSeedType.deserialize(raw: _nanoSeedType) : defaultNanoSeedType;
|
||||
|
||||
balanceDisplayMode = BalanceDisplayMode.deserialize(
|
||||
raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
|
||||
shouldSaveRecipientAddress =
|
||||
|
|
|
@ -13,8 +13,15 @@ class CWTron extends Tron {
|
|||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
String? mnemonic,
|
||||
String? parentAddress,
|
||||
}) =>
|
||||
TronNewWalletCredentials(name: name, walletInfo: walletInfo, password: password);
|
||||
TronNewWalletCredentials(
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
parentAddress: parentAddress);
|
||||
|
||||
@override
|
||||
WalletCredentials createTronRestoreWalletFromSeedCredentials({
|
||||
|
@ -34,7 +41,7 @@ class CWTron extends Tron {
|
|||
|
||||
@override
|
||||
String getAddress(WalletBase wallet) => (wallet as TronWallet).walletAddresses.address;
|
||||
|
||||
|
||||
Object createTronTransactionCredentials(
|
||||
List<Output> outputs, {
|
||||
required CryptoCurrency currency,
|
||||
|
@ -63,10 +70,10 @@ class CWTron extends Tron {
|
|||
|
||||
@override
|
||||
Future<void> addTronToken(WalletBase wallet, CryptoCurrency token, String contractAddress) async {
|
||||
final tronToken = TronToken(
|
||||
final tronToken = TronToken(
|
||||
name: token.name,
|
||||
symbol: token.title,
|
||||
contractAddress: contractAddress,
|
||||
contractAddress: contractAddress,
|
||||
decimal: token.decimals,
|
||||
enabled: token.enabled,
|
||||
iconPath: token.iconPath,
|
||||
|
|
|
@ -46,17 +46,30 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
|||
case WalletType.litecoin:
|
||||
return _settingsStore.bitcoinSeedType == BitcoinSeedType.bip39;
|
||||
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return _settingsStore.nanoSeedType == NanoSeedType.bip39;
|
||||
|
||||
case WalletType.monero:
|
||||
case WalletType.wownero:
|
||||
case WalletType.none:
|
||||
case WalletType.haven:
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool get hasSeedTypeOption => [WalletType.monero, WalletType.wownero].contains(type);
|
||||
|
||||
bool get isMoneroSeedTypeOptionsEnabled => [
|
||||
WalletType.monero,
|
||||
WalletType.wownero,
|
||||
].contains(type);
|
||||
|
||||
bool get isBitcoinSeedTypeOptionsEnabled => [
|
||||
WalletType.bitcoin,
|
||||
WalletType.litecoin,
|
||||
].contains(type);
|
||||
|
||||
bool get isNanoSeedTypeOptionsEnabled => [WalletType.nano].contains(type);
|
||||
|
||||
bool get hasPassphraseOption => [
|
||||
WalletType.bitcoin,
|
||||
|
|
|
@ -369,14 +369,14 @@ abstract class BalanceViewModelBase with Store {
|
|||
}
|
||||
|
||||
bool _hasSecondAdditionalBalanceForWalletType(WalletType type) {
|
||||
if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) {
|
||||
if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _hasSecondAvailableBalanceForWalletType(WalletType type) {
|
||||
if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) {
|
||||
if (wallet.type == WalletType.litecoin && settingsStore.mwebEnabled) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -88,6 +88,11 @@ abstract class DashboardViewModelBase with Store {
|
|||
value: () => transactionFilterStore.displayOutgoing,
|
||||
caption: S.current.outgoing,
|
||||
onChanged: transactionFilterStore.toggleOutgoing),
|
||||
FilterItem(
|
||||
value: () => transactionFilterStore.displaySilentPayments,
|
||||
caption: S.current.silent_payments,
|
||||
onChanged: transactionFilterStore.toggleSilentPayments,
|
||||
),
|
||||
// FilterItem(
|
||||
// value: () => false,
|
||||
// caption: S.current.transactions_by_date,
|
||||
|
@ -132,8 +137,8 @@ abstract class DashboardViewModelBase with Store {
|
|||
FilterItem(
|
||||
value: () => tradeFilterStore.displayLetsExchange,
|
||||
caption: ExchangeProviderDescription.letsExchange.title,
|
||||
onChanged: () =>
|
||||
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)),
|
||||
onChanged: () => tradeFilterStore
|
||||
.toggleDisplayExchange(ExchangeProviderDescription.letsExchange)),
|
||||
FilterItem(
|
||||
value: () => tradeFilterStore.displayStealthEx,
|
||||
caption: ExchangeProviderDescription.stealthEx.title,
|
||||
|
@ -443,6 +448,7 @@ abstract class DashboardViewModelBase with Store {
|
|||
settingsStore.hasEnabledMwebBefore = true;
|
||||
}
|
||||
|
||||
settingsStore.mwebEnabled = active;
|
||||
mwebScanningActive = active;
|
||||
bitcoin!.setMwebEnabled(wallet, active);
|
||||
}
|
||||
|
@ -801,6 +807,16 @@ abstract class DashboardViewModelBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
String getTransactionType(TransactionInfo tx) {
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
if (tx.isReplaced == true) return ' (replaced)';
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.ethereum && tx.evmSignatureName == 'approval')
|
||||
return ' (${tx.evmSignatureName})';
|
||||
return '';
|
||||
}
|
||||
|
||||
Future<void> refreshDashboard() async {
|
||||
reconnect();
|
||||
}
|
||||
|
|
|
@ -85,18 +85,6 @@ class TransactionListItem extends ActionListItem with Keyable {
|
|||
return '';
|
||||
}
|
||||
|
||||
String get tag {
|
||||
List<String> addresses =
|
||||
(transaction.inputAddresses ?? []) + (transaction.outputAddresses ?? []);
|
||||
for (var address in addresses) {
|
||||
if (address.toLowerCase().contains('mweb')) {
|
||||
return 'MWEB';
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
CryptoCurrency? get assetOfTransaction {
|
||||
try {
|
||||
if (balanceViewModel.wallet.type == WalletType.ethereum) {
|
||||
|
|
16
lib/view_model/new_wallet_type_view_model.dart
Normal file
16
lib/view_model/new_wallet_type_view_model.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'new_wallet_type_view_model.g.dart';
|
||||
|
||||
class NewWalletTypeViewModel = NewWalletTypeViewModelBase with _$NewWalletTypeViewModel;
|
||||
|
||||
abstract class NewWalletTypeViewModelBase with Store {
|
||||
NewWalletTypeViewModelBase(this._walletInfoSource);
|
||||
|
||||
@computed
|
||||
bool get hasExisitingWallet => _walletInfoSource.isNotEmpty;
|
||||
|
||||
final Box<WalletInfo> _walletInfoSource;
|
||||
}
|
|
@ -31,6 +31,9 @@ abstract class RescanViewModelBase with Store {
|
|||
|
||||
@computed
|
||||
bool get isMwebScan => wallet.type == WalletType.litecoin;
|
||||
|
||||
Future<bool> get isBitcoinMempoolAPIEnabled async =>
|
||||
wallet.type == WalletType.bitcoin && await bitcoin!.checkIfMempoolAPIIsEnabled(wallet);
|
||||
|
||||
@action
|
||||
Future<void> rescanCurrentWallet({required int restoreHeight}) async {
|
||||
|
|
|
@ -23,6 +23,13 @@ abstract class SeedSettingsViewModelBase with Store {
|
|||
void setBitcoinSeedType(BitcoinSeedType derivationType) =>
|
||||
_appStore.settingsStore.bitcoinSeedType = derivationType;
|
||||
|
||||
@computed
|
||||
NanoSeedType get nanoSeedType => _appStore.settingsStore.nanoSeedType;
|
||||
|
||||
@action
|
||||
void setNanoSeedType(NanoSeedType derivationType) =>
|
||||
_appStore.settingsStore.nanoSeedType = derivationType;
|
||||
|
||||
@computed
|
||||
String? get passphrase => this._seedSettingsStore.passphrase;
|
||||
|
||||
|
|
|
@ -79,6 +79,9 @@ abstract class OutputBase with Store {
|
|||
bool get isParsedAddress =>
|
||||
parsedAddress.parseFrom != ParseFrom.notParsed && parsedAddress.name.isNotEmpty;
|
||||
|
||||
@observable
|
||||
String? stealthAddress;
|
||||
|
||||
@computed
|
||||
int get formattedCryptoAmount {
|
||||
int amount = 0;
|
||||
|
@ -134,9 +137,8 @@ abstract class OutputBase with Store {
|
|||
final trc20EstimatedFee = tron!.getTronTRC20EstimatedFee(_wallet) ?? 0;
|
||||
return double.parse(trc20EstimatedFee.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (_wallet.type == WalletType.solana) {
|
||||
return solana!.getEstimateFees(_wallet) ?? 0.0;
|
||||
}
|
||||
|
@ -145,16 +147,16 @@ abstract class OutputBase with Store {
|
|||
_settingsStore.priority[_wallet.type]!, formattedCryptoAmount);
|
||||
|
||||
if (_wallet.type == WalletType.bitcoin) {
|
||||
if (_settingsStore.priority[_wallet.type] == bitcoin!.getBitcoinTransactionPriorityCustom()) {
|
||||
fee = bitcoin!.getEstimatedFeeWithFeeRate(_wallet,
|
||||
_settingsStore.customBitcoinFeeRate,formattedCryptoAmount);
|
||||
if (_settingsStore.priority[_wallet.type] ==
|
||||
bitcoin!.getBitcoinTransactionPriorityCustom()) {
|
||||
fee = bitcoin!.getEstimatedFeeWithFeeRate(
|
||||
_wallet, _settingsStore.customBitcoinFeeRate, formattedCryptoAmount);
|
||||
}
|
||||
|
||||
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
if (_wallet.type == WalletType.litecoin ||
|
||||
_wallet.type == WalletType.bitcoinCash) {
|
||||
if (_wallet.type == WalletType.litecoin || _wallet.type == WalletType.bitcoinCash) {
|
||||
return bitcoin!.formatterBitcoinAmountToDouble(amount: fee);
|
||||
}
|
||||
|
||||
|
@ -249,7 +251,8 @@ abstract class OutputBase with Store {
|
|||
try {
|
||||
final fiat = calculateFiatAmount(
|
||||
price: _fiatConversationStore.prices[cryptoCurrencyHandler()]!,
|
||||
cryptoAmount: sendAll ? cryptoFullBalance.replaceAll(",", ".") : cryptoAmount.replaceAll(',', '.'));
|
||||
cryptoAmount:
|
||||
sendAll ? cryptoFullBalance.replaceAll(",", ".") : cryptoAmount.replaceAll(',', '.'));
|
||||
if (fiatAmount != fiat) {
|
||||
fiatAmount = fiat;
|
||||
}
|
||||
|
|
|
@ -375,6 +375,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
throw Exception("THORChain does not support Taproot addresses");
|
||||
}
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
final updatedOutputs = bitcoin!.updateOutputs(pendingTransaction!, outputs);
|
||||
|
||||
if (outputs.length == updatedOutputs.length) {
|
||||
outputs = ObservableList.of(updatedOutputs);
|
||||
}
|
||||
}
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
return pendingTransaction;
|
||||
} catch (e) {
|
||||
|
@ -414,8 +423,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
}
|
||||
|
||||
Future<void> _executeReplaceByFee(TransactionInfo tx, String newFee) async {
|
||||
|
||||
|
||||
clearOutputs();
|
||||
final output = outputs.first;
|
||||
output.address = tx.outputAddresses?.first ?? '';
|
||||
|
|
|
@ -36,7 +36,8 @@ abstract class TransactionDetailsViewModelBase with Store {
|
|||
required this.transactionDescriptionBox,
|
||||
required this.wallet,
|
||||
required this.settingsStore,
|
||||
required this.sendViewModel})
|
||||
required this.sendViewModel,
|
||||
this.canReplaceByFee = false})
|
||||
: items = [],
|
||||
RBFListItems = [],
|
||||
newFee = 0,
|
||||
|
@ -51,8 +52,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
|||
break;
|
||||
case WalletType.bitcoin:
|
||||
_addElectrumListItems(tx, dateFormat);
|
||||
_addBumpFeesListItems(tx);
|
||||
_checkForRBF(tx);
|
||||
if(!canReplaceByFee)_checkForRBF(tx);
|
||||
break;
|
||||
case WalletType.litecoin:
|
||||
case WalletType.bitcoinCash:
|
||||
|
@ -139,13 +139,11 @@ abstract class TransactionDetailsViewModelBase with Store {
|
|||
bool showRecipientAddress;
|
||||
bool isRecipientAddressShown;
|
||||
int newFee;
|
||||
String? rawTransaction;
|
||||
TransactionPriority? transactionPriority;
|
||||
|
||||
@observable
|
||||
bool _canReplaceByFee = false;
|
||||
|
||||
@computed
|
||||
bool get canReplaceByFee => _canReplaceByFee /*&& transactionInfo.confirmations <= 0*/;
|
||||
bool canReplaceByFee;
|
||||
|
||||
String _explorerUrl(WalletType type, String txId) {
|
||||
switch (type) {
|
||||
|
@ -347,7 +345,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
|||
items.addAll(_items);
|
||||
}
|
||||
|
||||
void _addBumpFeesListItems(TransactionInfo tx) {
|
||||
void addBumpFeesListItems(TransactionInfo tx, String rawTransaction) {
|
||||
transactionPriority = bitcoin!.getBitcoinTransactionPriorityMedium();
|
||||
final inputsCount = (transactionInfo.inputAddresses?.isEmpty ?? true)
|
||||
? 1
|
||||
|
@ -361,6 +359,14 @@ abstract class TransactionDetailsViewModelBase with Store {
|
|||
|
||||
RBFListItems.add(StandartListItem(title: S.current.old_fee, value: tx.feeFormatted() ?? '0.0'));
|
||||
|
||||
if (transactionInfo.fee != null && rawTransaction.isNotEmpty) {
|
||||
final size = bitcoin!.getTransactionVSize(wallet, rawTransaction);
|
||||
final recommendedRate = (transactionInfo.fee! / size).round() + 1;
|
||||
|
||||
RBFListItems.add(
|
||||
StandartListItem(title: 'New recommended fee rate', value: '$recommendedRate sat/byte'));
|
||||
}
|
||||
|
||||
final priorities = priorityForWalletType(wallet.type);
|
||||
final selectedItem = priorities.indexOf(sendViewModel.transactionPriority);
|
||||
final customItem = priorities
|
||||
|
@ -429,8 +435,9 @@ abstract class TransactionDetailsViewModelBase with Store {
|
|||
Future<void> _checkForRBF(TransactionInfo tx) async {
|
||||
if (wallet.type == WalletType.bitcoin &&
|
||||
transactionInfo.direction == TransactionDirection.outgoing) {
|
||||
if (await bitcoin!.canReplaceByFee(wallet, tx)) {
|
||||
_canReplaceByFee = true;
|
||||
rawTransaction = await bitcoin!.canReplaceByFee(wallet, tx);
|
||||
if (rawTransaction != null) {
|
||||
canReplaceByFee = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ abstract class WalletCreationVMBase with Store {
|
|||
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
||||
derivationInfo: credentials.derivationInfo ?? getDefaultCreateDerivation(),
|
||||
hardwareWalletType: credentials.hardwareWalletType,
|
||||
parentAddress: credentials.parentAddress,
|
||||
);
|
||||
|
||||
credentials.walletInfo = walletInfo;
|
||||
|
@ -117,12 +118,16 @@ abstract class WalletCreationVMBase with Store {
|
|||
}
|
||||
|
||||
DerivationInfo? getDefaultCreateDerivation() {
|
||||
final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
|
||||
final useBip39ForBitcoin = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39;
|
||||
final useBip39ForNano = seedSettingsViewModel.nanoSeedType.type == DerivationType.bip39;
|
||||
switch (type) {
|
||||
case WalletType.nano:
|
||||
if (useBip39ForNano) {
|
||||
return DerivationInfo(derivationType: DerivationType.bip39);
|
||||
}
|
||||
return DerivationInfo(derivationType: DerivationType.nano);
|
||||
case WalletType.bitcoin:
|
||||
if (useBip39) {
|
||||
if (useBip39ForBitcoin) {
|
||||
return DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/0'/0'",
|
||||
|
@ -132,7 +137,7 @@ abstract class WalletCreationVMBase with Store {
|
|||
}
|
||||
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||
case WalletType.litecoin:
|
||||
if (useBip39) {
|
||||
if (useBip39ForBitcoin) {
|
||||
return DerivationInfo(
|
||||
derivationType: DerivationType.bip39,
|
||||
derivationPath: "m/84'/2'/0'",
|
||||
|
@ -148,9 +153,13 @@ abstract class WalletCreationVMBase with Store {
|
|||
|
||||
DerivationInfo? getCommonRestoreDerivation() {
|
||||
final useElectrum = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.electrum;
|
||||
final useNanoStandard = seedSettingsViewModel.nanoSeedType.type == DerivationType.nano;
|
||||
switch (this.type) {
|
||||
case WalletType.nano:
|
||||
return DerivationInfo(derivationType: DerivationType.nano);
|
||||
if (useNanoStandard) {
|
||||
return DerivationInfo(derivationType: DerivationType.nano);
|
||||
}
|
||||
return DerivationInfo(derivationType: DerivationType.bip39);
|
||||
case WalletType.bitcoin:
|
||||
if (useElectrum) {
|
||||
return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first;
|
||||
|
|
165
lib/view_model/wallet_groups_display_view_model.dart
Normal file
165
lib/view_model/wallet_groups_display_view_model.dart
Normal file
|
@ -0,0 +1,165 @@
|
|||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_group.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:cake_wallet/reactions/bip39_wallet_utils.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'wallet_groups_display_view_model.g.dart';
|
||||
|
||||
class WalletGroupsDisplayViewModel = WalletGroupsDisplayViewModelBase
|
||||
with _$WalletGroupsDisplayViewModel;
|
||||
|
||||
abstract class WalletGroupsDisplayViewModelBase with Store {
|
||||
WalletGroupsDisplayViewModelBase(
|
||||
this._appStore,
|
||||
this._walletLoadingService,
|
||||
this._walletManager,
|
||||
this.walletListViewModel, {
|
||||
required this.type,
|
||||
}) : isFetchingMnemonic = false,
|
||||
multiWalletGroups = ObservableList<WalletGroup>(),
|
||||
singleWalletsList = ObservableList<WalletInfo>() {
|
||||
reaction((_) => _appStore.wallet, (_) => updateWalletInfoSourceList());
|
||||
updateWalletInfoSourceList();
|
||||
}
|
||||
|
||||
final WalletType type;
|
||||
final AppStore _appStore;
|
||||
final WalletManager _walletManager;
|
||||
final WalletLoadingService _walletLoadingService;
|
||||
final WalletListViewModel walletListViewModel;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletGroup> multiWalletGroups;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletInfo> singleWalletsList;
|
||||
|
||||
@observable
|
||||
WalletGroup? selectedWalletGroup;
|
||||
|
||||
@observable
|
||||
WalletInfo? selectedSingleWallet;
|
||||
|
||||
@observable
|
||||
String? parentAddress;
|
||||
|
||||
@observable
|
||||
bool isFetchingMnemonic;
|
||||
|
||||
@computed
|
||||
bool get hasNoFilteredWallet {
|
||||
return singleWalletsList.isEmpty && multiWalletGroups.isEmpty;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<String?> getSelectedWalletMnemonic() async {
|
||||
WalletListItem walletToUse;
|
||||
|
||||
bool isGroupSelected = selectedWalletGroup != null;
|
||||
|
||||
if (isGroupSelected) {
|
||||
walletToUse = convertWalletInfoToWalletListItem(selectedWalletGroup!.wallets.first);
|
||||
} else {
|
||||
walletToUse = convertWalletInfoToWalletListItem(selectedSingleWallet!);
|
||||
}
|
||||
|
||||
try {
|
||||
isFetchingMnemonic = true;
|
||||
final wallet = await _walletLoadingService.load(
|
||||
walletToUse.type,
|
||||
walletToUse.name,
|
||||
);
|
||||
|
||||
parentAddress =
|
||||
isGroupSelected ? selectedWalletGroup!.parentAddress : selectedSingleWallet!.address;
|
||||
|
||||
return wallet.seed;
|
||||
} catch (e) {
|
||||
return null;
|
||||
} finally {
|
||||
isFetchingMnemonic = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void selectWalletGroup(WalletGroup walletGroup) {
|
||||
selectedWalletGroup = walletGroup;
|
||||
selectedSingleWallet = null;
|
||||
}
|
||||
|
||||
@action
|
||||
void selectSingleWallet(WalletInfo singleWallet) {
|
||||
selectedSingleWallet = singleWallet;
|
||||
selectedWalletGroup = null;
|
||||
}
|
||||
|
||||
@action
|
||||
void updateWalletInfoSourceList() {
|
||||
List<WalletGroup> wallets = [];
|
||||
|
||||
multiWalletGroups.clear();
|
||||
singleWalletsList.clear();
|
||||
|
||||
_walletManager.updateWalletGroups();
|
||||
|
||||
final walletGroups = _walletManager.walletGroups;
|
||||
|
||||
// Iterate through the wallet groups to filter and categorize wallets
|
||||
for (var group in walletGroups) {
|
||||
// Handle group wallet filtering
|
||||
bool shouldExcludeGroup = group.wallets.any((wallet) {
|
||||
// Check for non-BIP39 wallet types
|
||||
bool isNonBIP39Wallet = !isBIP39Wallet(wallet.type);
|
||||
|
||||
// Check for nano derivation type
|
||||
bool isNanoDerivationType = wallet.type == WalletType.nano &&
|
||||
wallet.derivationInfo?.derivationType == DerivationType.nano;
|
||||
|
||||
// Check for electrum derivation type
|
||||
bool isElectrumDerivationType =
|
||||
(wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) &&
|
||||
wallet.derivationInfo?.derivationType == DerivationType.electrum;
|
||||
|
||||
// Check that selected wallet type is not present already in group
|
||||
bool isSameTypeAsSelectedWallet = wallet.type == type;
|
||||
|
||||
// Exclude if any of these conditions are true
|
||||
return isNonBIP39Wallet ||
|
||||
isNanoDerivationType ||
|
||||
isElectrumDerivationType ||
|
||||
isSameTypeAsSelectedWallet;
|
||||
});
|
||||
|
||||
if (shouldExcludeGroup) continue;
|
||||
|
||||
// If the group passes the filters, add it to the wallets list
|
||||
wallets.add(group);
|
||||
}
|
||||
|
||||
for (var group in wallets) {
|
||||
if (group.wallets.length == 1) {
|
||||
singleWalletsList.add(group.wallets.first);
|
||||
} else {
|
||||
multiWalletGroups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WalletListItem convertWalletInfoToWalletListItem(WalletInfo info) {
|
||||
return WalletListItem(
|
||||
name: info.name,
|
||||
type: info.type,
|
||||
key: info.key,
|
||||
isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
|
||||
isEnabled: availableWalletTypes.contains(info.type),
|
||||
isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
|
@ -18,8 +19,11 @@ class WalletEditRenamePending extends WalletEditViewModelState {}
|
|||
class WalletEditDeletePending extends WalletEditViewModelState {}
|
||||
|
||||
abstract class WalletEditViewModelBase with Store {
|
||||
WalletEditViewModelBase(this._walletListViewModel, this._walletLoadingService)
|
||||
: state = WalletEditViewModelInitialState(),
|
||||
WalletEditViewModelBase(
|
||||
this._walletListViewModel,
|
||||
this._walletLoadingService,
|
||||
this._walletManager,
|
||||
) : state = WalletEditViewModelInitialState(),
|
||||
newName = '';
|
||||
|
||||
@observable
|
||||
|
@ -30,13 +34,30 @@ abstract class WalletEditViewModelBase with Store {
|
|||
|
||||
final WalletListViewModel _walletListViewModel;
|
||||
final WalletLoadingService _walletLoadingService;
|
||||
final WalletManager _walletManager;
|
||||
|
||||
@action
|
||||
Future<void> changeName(WalletListItem walletItem, {String? password}) async {
|
||||
Future<void> changeName(
|
||||
WalletListItem walletItem, {
|
||||
String? password,
|
||||
String? groupParentAddress,
|
||||
bool isWalletGroup = false,
|
||||
}) async {
|
||||
state = WalletEditRenamePending();
|
||||
await _walletLoadingService.renameWallet(
|
||||
walletItem.type, walletItem.name, newName,
|
||||
password: password);
|
||||
|
||||
if (isWalletGroup) {
|
||||
_walletManager.updateWalletGroups();
|
||||
|
||||
_walletManager.setGroupName(groupParentAddress!, newName);
|
||||
} else {
|
||||
await _walletLoadingService.renameWallet(
|
||||
walletItem.type,
|
||||
walletItem.name,
|
||||
newName,
|
||||
password: password,
|
||||
);
|
||||
}
|
||||
|
||||
_walletListViewModel.updateList();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_group.dart';
|
||||
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
|
||||
import 'package:cake_wallet/entities/wallet_manager.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
|
@ -17,7 +19,10 @@ abstract class WalletListViewModelBase with Store {
|
|||
this._walletInfoSource,
|
||||
this._appStore,
|
||||
this._walletLoadingService,
|
||||
) : wallets = ObservableList<WalletListItem>() {
|
||||
this._walletManager,
|
||||
) : wallets = ObservableList<WalletListItem>(),
|
||||
multiWalletGroups = ObservableList<WalletGroup>(),
|
||||
singleWalletsList = ObservableList<WalletListItem>() {
|
||||
setOrderType(_appStore.settingsStore.walletListOrder);
|
||||
reaction((_) => _appStore.wallet, (_) => updateList());
|
||||
updateList();
|
||||
|
@ -26,6 +31,15 @@ abstract class WalletListViewModelBase with Store {
|
|||
@observable
|
||||
ObservableList<WalletListItem> wallets;
|
||||
|
||||
// @observable
|
||||
// ObservableList<WalletGroup> walletGroups;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletGroup> multiWalletGroups;
|
||||
|
||||
@observable
|
||||
ObservableList<WalletListItem> singleWalletsList;
|
||||
|
||||
@computed
|
||||
bool get shouldRequireTOTP2FAForAccessingWallet =>
|
||||
_appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
|
||||
|
@ -35,6 +49,7 @@ abstract class WalletListViewModelBase with Store {
|
|||
_appStore.settingsStore.shouldRequireTOTP2FAForCreatingNewWallets;
|
||||
|
||||
final AppStore _appStore;
|
||||
final WalletManager _walletManager;
|
||||
final Box<WalletInfo> _walletInfoSource;
|
||||
final WalletLoadingService _walletLoadingService;
|
||||
|
||||
|
@ -53,18 +68,23 @@ abstract class WalletListViewModelBase with Store {
|
|||
@action
|
||||
void updateList() {
|
||||
wallets.clear();
|
||||
multiWalletGroups.clear();
|
||||
singleWalletsList.clear();
|
||||
|
||||
wallets.addAll(
|
||||
_walletInfoSource.values.map(
|
||||
(info) => WalletListItem(
|
||||
name: info.name,
|
||||
type: info.type,
|
||||
key: info.key,
|
||||
isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
|
||||
isEnabled: availableWalletTypes.contains(info.type),
|
||||
isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
|
||||
),
|
||||
),
|
||||
_walletInfoSource.values.map((info) => convertWalletInfoToWalletListItem(info)),
|
||||
);
|
||||
|
||||
//========== Split into shared seed groups and single wallets list
|
||||
_walletManager.updateWalletGroups();
|
||||
|
||||
for (var group in _walletManager.walletGroups) {
|
||||
if (group.wallets.length == 1) {
|
||||
singleWalletsList.add(convertWalletInfoToWalletListItem(group.wallets.first));
|
||||
} else {
|
||||
multiWalletGroups.add(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> reorderAccordingToWalletList() async {
|
||||
|
@ -158,4 +178,15 @@ abstract class WalletListViewModelBase with Store {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WalletListItem convertWalletInfoToWalletListItem(WalletInfo info) {
|
||||
return WalletListItem(
|
||||
name: info.name,
|
||||
type: info.type,
|
||||
key: info.key,
|
||||
isCurrent: info.name == _appStore.wallet?.name && info.type == _appStore.wallet?.type,
|
||||
isEnabled: availableWalletTypes.contains(info.type),
|
||||
isTestnet: info.network?.toLowerCase().contains('testnet') ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
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/core/wallet_creation_service.dart';
|
||||
import 'package:cake_wallet/entities/seed_type.dart';
|
||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||
import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/nano/nano.dart';
|
||||
|
@ -28,16 +29,17 @@ class WalletNewVM = WalletNewVMBase with _$WalletNewVM;
|
|||
|
||||
abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||
WalletNewVMBase(
|
||||
AppStore appStore,
|
||||
WalletCreationService walletCreationService,
|
||||
Box<WalletInfo> walletInfoSource,
|
||||
this.advancedPrivacySettingsViewModel,
|
||||
SeedSettingsViewModel seedSettingsViewModel,
|
||||
{required WalletType type})
|
||||
: selectedMnemonicLanguage = '',
|
||||
AppStore appStore,
|
||||
WalletCreationService walletCreationService,
|
||||
Box<WalletInfo> walletInfoSource,
|
||||
this.advancedPrivacySettingsViewModel,
|
||||
SeedSettingsViewModel seedSettingsViewModel, {
|
||||
required this.newWalletArguments,
|
||||
}) : selectedMnemonicLanguage = '',
|
||||
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
|
||||
type: type, isRecovery: false);
|
||||
type: newWalletArguments!.type, isRecovery: false);
|
||||
|
||||
final NewWalletArguments? newWalletArguments;
|
||||
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
|
||||
|
||||
@observable
|
||||
|
@ -62,6 +64,10 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
|||
return seedSettingsViewModel.bitcoinSeedType == BitcoinSeedType.bip39
|
||||
? advancedPrivacySettingsViewModel.seedPhraseLength.value
|
||||
: 24;
|
||||
case WalletType.nano:
|
||||
return seedSettingsViewModel.nanoSeedType == NanoSeedType.bip39
|
||||
? advancedPrivacySettingsViewModel.seedPhraseLength.value
|
||||
: 24;
|
||||
default:
|
||||
return 24;
|
||||
}
|
||||
|
@ -83,31 +89,68 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
|||
password: walletPassword,
|
||||
isPolyseed: options.last as bool);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.createBitcoinNewWalletCredentials(
|
||||
name: name, password: walletPassword, passphrase: passphrase);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.createBitcoinNewWalletCredentials(
|
||||
name: name, password: walletPassword, passphrase: passphrase);
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
passphrase: passphrase,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.haven:
|
||||
return haven!.createHavenNewWalletCredentials(
|
||||
name: name, language: options!.first as String, password: walletPassword);
|
||||
case WalletType.ethereum:
|
||||
return ethereum!.createEthereumNewWalletCredentials(name: name, password: walletPassword);
|
||||
return ethereum!.createEthereumNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoinCash!.createBitcoinCashNewWalletCredentials(
|
||||
name: name, password: walletPassword, passphrase: passphrase);
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
passphrase: passphrase,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.nano:
|
||||
case WalletType.banano:
|
||||
return nano!.createNanoNewWalletCredentials(name: name);
|
||||
return nano!.createNanoNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.polygon:
|
||||
return polygon!.createPolygonNewWalletCredentials(name: name, password: walletPassword);
|
||||
return polygon!.createPolygonNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.solana:
|
||||
return solana!.createSolanaNewWalletCredentials(name: name, password: walletPassword);
|
||||
return solana!.createSolanaNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.tron:
|
||||
return tron!.createTronNewWalletCredentials(name: name);
|
||||
return tron!.createTronNewWalletCredentials(
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
mnemonic: newWalletArguments!.mnemonic,
|
||||
parentAddress: newWalletArguments!.parentAddress,
|
||||
);
|
||||
case WalletType.wownero:
|
||||
return wownero!.createWowneroNewWalletCredentials(
|
||||
name: name, language: options!.first as String, isPolyseed: options.last as bool);
|
||||
name: name,
|
||||
password: walletPassword,
|
||||
language: options!.first as String,
|
||||
isPolyseed: options.last as bool,
|
||||
);
|
||||
case WalletType.none:
|
||||
throw Exception('Unexpected type: ${type.toString()}');
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ dependency_overrides:
|
|||
bitcoin_base:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_base
|
||||
ref: cake-update-v6
|
||||
ref: cake-update-v8
|
||||
ffi: 2.1.0
|
||||
|
||||
flutter_icons:
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
"choose_from_available_options": "اختر من بين الخيارات المتاحة:",
|
||||
"choose_one": "اختر واحدة",
|
||||
"choose_relay": "ﻡﺍﺪﺨﺘﺳﻼﻟ ﻊﺑﺎﺘﺘﻟﺍ ﺭﺎﻴﺘﺧﺍ ءﺎﺟﺮﻟﺍ",
|
||||
"choose_wallet_currency": "الرجاء اختيار عملة المحفظة:",
|
||||
"choose_wallet_group": "اختر مجموعة المحفظة",
|
||||
"clear": "مسح",
|
||||
"clearnet_link": "رابط Clearnet",
|
||||
"close": "يغلق",
|
||||
|
@ -176,6 +176,7 @@
|
|||
"create_invoice": "إنشاء فاتورة",
|
||||
"create_new": "إنشاء محفظة جديدة",
|
||||
"create_new_account": "انشاء حساب جديد",
|
||||
"create_new_seed": "إنشاء بذرة جديدة",
|
||||
"creating_new_wallet": "يتم إنشاء محفظة جديدة",
|
||||
"creating_new_wallet_error": "خطأ: ${description}",
|
||||
"creation_date": "تاريخ الإنشاء",
|
||||
|
@ -232,8 +233,9 @@
|
|||
"edit_token": "تحرير الرمز المميز",
|
||||
"electrum_address_disclaimer": "نقوم بإنشاء عناوين جديدة في كل مرة تستخدم فيها عنوانًا ، لكن العناوين السابقة تستمر في العمل",
|
||||
"email_address": "عنوان البريد الالكترونى",
|
||||
"enable_mempool_api": "MEMPOOL API للحصول على رسوم وتواريخ دقيقة",
|
||||
"enable_replace_by_fee": "تمكين الاستبدال",
|
||||
"enable_silent_payments_scanning": "تمكين المسح الضوئي للمدفوعات الصامتة",
|
||||
"enable_silent_payments_scanning": "ابدأ في مسح المدفوعات الصامتة ، حتى يتم الوصول إلى الطرف",
|
||||
"enabled": "ممكنة",
|
||||
"enter_amount": "أدخل المبلغ",
|
||||
"enter_backup_password": "أدخل كلمة المرور الاحتياطية هنا",
|
||||
|
@ -295,6 +297,7 @@
|
|||
"failed_authentication": "${state_error} فشل المصادقة.",
|
||||
"faq": "الأسئلة الشائعة",
|
||||
"features": "سمات",
|
||||
"fee_rate": "معدل الرسوم",
|
||||
"fetching": "جار الجلب",
|
||||
"fiat_api": "Fiat API",
|
||||
"fiat_balance": "الرصيد فيات",
|
||||
|
@ -608,6 +611,8 @@
|
|||
"seed_share": "شارك السييد",
|
||||
"seed_title": "سييد",
|
||||
"seedtype": "البذور",
|
||||
"seedtype_alert_content": "مشاركة البذور مع محافظ أخرى ممكن فقط مع BIP39 Seedtype.",
|
||||
"seedtype_alert_title": "تنبيه البذور",
|
||||
"seedtype_legacy": "إرث (25 كلمة)",
|
||||
"seedtype_polyseed": "بوليسيد (16 كلمة)",
|
||||
"select_backup_file": "حدد ملف النسخ الاحتياطي",
|
||||
|
@ -620,6 +625,7 @@
|
|||
"send": "إرسال",
|
||||
"send_address": "عنوان ${cryptoCurrency}",
|
||||
"send_amount": "مقدار:",
|
||||
"send_change_to_you": "تغيير لك:",
|
||||
"send_creating_transaction": " يتم إنشاء المعاملة",
|
||||
"send_error_currency": "العملة يجب أن تحتوي على أرقام فقط",
|
||||
"send_error_minimum_value": "الحد الأدنى لقيمة المبلغ هو 0.01",
|
||||
|
@ -673,6 +679,7 @@
|
|||
"setup_your_debit_card": "قم بإعداد بطاقة ائتمان الخاصة بك",
|
||||
"share": "يشارك",
|
||||
"share_address": "شارك العنوان",
|
||||
"shared_seed_wallet_groups": "مجموعات محفظة البذور المشتركة",
|
||||
"show_details": "اظهر التفاصيل",
|
||||
"show_keys": "اظهار السييد / المفاتيح",
|
||||
"show_market_place": "إظهار السوق",
|
||||
|
@ -686,6 +693,7 @@
|
|||
"signature_invalid_error": "التوقيع غير صالح للرسالة المقدمة",
|
||||
"signTransaction": " ﺔﻠﻣﺎﻌﻤﻟﺍ ﻊﻴﻗﻮﺗ",
|
||||
"signup_for_card_accept_terms": "قم بالتسجيل للحصول على البطاقة وقبول الشروط.",
|
||||
"silent_payment": "الدفع الصامت",
|
||||
"silent_payments": "مدفوعات صامتة",
|
||||
"silent_payments_always_scan": "حدد المدفوعات الصامتة دائمًا المسح الضوئي",
|
||||
"silent_payments_disclaimer": "العناوين الجديدة ليست هويات جديدة. إنها إعادة استخدام هوية موجودة مع ملصق مختلف.",
|
||||
|
@ -696,6 +704,7 @@
|
|||
"silent_payments_scanned_tip": "ممسوح ليفحص! (${tip})",
|
||||
"silent_payments_scanning": "المدفوعات الصامتة المسح الضوئي",
|
||||
"silent_payments_settings": "إعدادات المدفوعات الصامتة",
|
||||
"single_seed_wallets_group": "محافظ بذرة واحدة",
|
||||
"slidable": "قابل للانزلاق",
|
||||
"sort_by": "ترتيب حسب",
|
||||
"spend_key_private": "مفتاح الإنفاق (خاص)",
|
||||
|
@ -718,12 +727,13 @@
|
|||
"switchToEVMCompatibleWallet": " (Ethereum، Polygon) ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ EVM ﻊﻣ ﺔﻘﻓﺍﻮﺘﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ",
|
||||
"symbol": "ﺰﻣﺭ",
|
||||
"sync_all_wallets": "مزامنة جميع المحافظ",
|
||||
"sync_status_attempting_scan": "محاولة المسح",
|
||||
"sync_status_attempting_sync": "جاري محاولة المزامنة",
|
||||
"sync_status_connected": "متصل",
|
||||
"sync_status_connecting": "يتم التوصيل",
|
||||
"sync_status_failed_connect": "انقطع الاتصال",
|
||||
"sync_status_not_connected": "غير متصل",
|
||||
"sync_status_starting_scan": "بدء المسح",
|
||||
"sync_status_starting_scan": "بدء المسح الضوئي (من ${height})",
|
||||
"sync_status_starting_sync": "بدء المزامنة",
|
||||
"sync_status_syncronized": "متزامن",
|
||||
"sync_status_syncronizing": "يتم المزامنة",
|
||||
|
@ -855,8 +865,16 @@
|
|||
"view_transaction_on": "عرض العملية على",
|
||||
"voting_weight": "وزن التصويت",
|
||||
"waitFewSecondForTxUpdate": "ﺕﻼﻣﺎﻌﻤﻟﺍ ﻞﺠﺳ ﻲﻓ ﺔﻠﻣﺎﻌﻤﻟﺍ ﺲﻜﻌﻨﺗ ﻰﺘﺣ ﻥﺍﻮﺛ ﻊﻀﺒﻟ ﺭﺎﻈﺘﻧﻻﺍ ﻰﺟﺮﻳ",
|
||||
"wallet_group": "مجموعة محفظة",
|
||||
"wallet_group_description_four": "لإنشاء محفظة مع بذرة جديدة تماما.",
|
||||
"wallet_group_description_one": "في محفظة الكيك ، يمكنك إنشاء ملف",
|
||||
"wallet_group_description_three": "لرؤية المحافظ المتاحة و/أو شاشة مجموعات المحفظة. أو اختر",
|
||||
"wallet_group_description_two": "عن طريق اختيار محفظة موجودة لتبادل البذور مع. يمكن أن تحتوي كل مجموعة محفظة على محفظة واحدة من كل نوع من العملة. \n\n يمكنك تحديدها",
|
||||
"wallet_group_empty_state_text_one": "يبدو أنه ليس لديك أي مجموعات محفظة متوافقة !\n\n انقر",
|
||||
"wallet_group_empty_state_text_two": "أدناه لجعل واحدة جديدة.",
|
||||
"wallet_keys": "سييد المحفظة / المفاتيح",
|
||||
"wallet_list_create_new_wallet": "إنشاء محفظة جديدة",
|
||||
"wallet_list_edit_group_name": "تحرير اسم المجموعة",
|
||||
"wallet_list_edit_wallet": "تحرير المحفظة",
|
||||
"wallet_list_failed_to_load": "فشل تحميل محفظة ${wallet_name}. ${error}",
|
||||
"wallet_list_failed_to_remove": "فشلت إزالة محفظة ${wallet_name}. ${error}",
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"choose_one": "Изберете едно",
|
||||
"choose_relay": "Моля, изберете реле, което да използвате",
|
||||
"choose_wallet_currency": "Изберете валута за портфейла:",
|
||||
"choose_wallet_group": "Изберете Group Wallet",
|
||||
"clear": "Изчисти",
|
||||
"clearnet_link": "Clearnet връзка",
|
||||
"close": "затвори",
|
||||
|
@ -176,6 +177,7 @@
|
|||
"create_invoice": "Създайте фактура",
|
||||
"create_new": "Създаване на нов портфейл",
|
||||
"create_new_account": "Създаване на нов профил",
|
||||
"create_new_seed": "Създайте нови семена",
|
||||
"creating_new_wallet": "Създаване на нов портфейл",
|
||||
"creating_new_wallet_error": "Грешка: ${description}",
|
||||
"creation_date": "Дата на създаване",
|
||||
|
@ -232,8 +234,9 @@
|
|||
"edit_token": "Редактиране на токена",
|
||||
"electrum_address_disclaimer": "Нови адреси се генерират всеки път, когато използвате този, но и предишните продължават да работят",
|
||||
"email_address": "Имейл адрес",
|
||||
"enable_mempool_api": "Mempool API за точни такси и дати",
|
||||
"enable_replace_by_fee": "Активиране на замяна по забрана",
|
||||
"enable_silent_payments_scanning": "Активирайте безшумните плащания за сканиране",
|
||||
"enable_silent_payments_scanning": "Започнете да сканирате безшумните плащания, докато се достигне съветът",
|
||||
"enabled": "Активирано",
|
||||
"enter_amount": "Въведете сума",
|
||||
"enter_backup_password": "Въведете парола за възстановяване",
|
||||
|
@ -295,6 +298,7 @@
|
|||
"failed_authentication": "Неуспешно удостоверяване. ${state_error}",
|
||||
"faq": "FAQ",
|
||||
"features": "Характеристика",
|
||||
"fee_rate": "Такса ставка",
|
||||
"fetching": "Обработване",
|
||||
"fiat_api": "Fiat API",
|
||||
"fiat_balance": "Фиат Баланс",
|
||||
|
@ -608,6 +612,8 @@
|
|||
"seed_share": "Споделяне на seed",
|
||||
"seed_title": "Seed",
|
||||
"seedtype": "Семенна тип",
|
||||
"seedtype_alert_content": "Споделянето на семена с други портфейли е възможно само с BIP39 Seedtype.",
|
||||
"seedtype_alert_title": "Сигнал за семена",
|
||||
"seedtype_legacy": "Наследство (25 думи)",
|
||||
"seedtype_polyseed": "Поли семе (16 думи)",
|
||||
"select_backup_file": "Избор на резервно копие",
|
||||
|
@ -620,6 +626,7 @@
|
|||
"send": "Изпрати",
|
||||
"send_address": "${cryptoCurrency} адрес",
|
||||
"send_amount": "Сума:",
|
||||
"send_change_to_you": "Променете, на вас:",
|
||||
"send_creating_transaction": "Създаване на транзакция",
|
||||
"send_error_currency": "Валутата може да съдържа само числа",
|
||||
"send_error_minimum_value": "Минималната сума е 0.01",
|
||||
|
@ -673,6 +680,7 @@
|
|||
"setup_your_debit_card": "Настройте своята дебитна карта",
|
||||
"share": "Дял",
|
||||
"share_address": "Сподели адрес",
|
||||
"shared_seed_wallet_groups": "Споделени групи за портфейли за семена",
|
||||
"show_details": "Показване на подробностите",
|
||||
"show_keys": "Покажи seed/keys",
|
||||
"show_market_place": "Покажи пазар",
|
||||
|
@ -686,6 +694,7 @@
|
|||
"signature_invalid_error": "Подписът не е валиден за даденото съобщение",
|
||||
"signTransaction": "Подпишете транзакция",
|
||||
"signup_for_card_accept_terms": "Регистрайте се за картата и приемете условията.",
|
||||
"silent_payment": "Безшумно плащане",
|
||||
"silent_payments": "Мълчаливи плащания",
|
||||
"silent_payments_always_scan": "Задайте мълчаливи плащания винаги сканиране",
|
||||
"silent_payments_disclaimer": "Новите адреси не са нови идентичности. Това е повторна употреба на съществуваща идентичност с различен етикет.",
|
||||
|
@ -696,6 +705,7 @@
|
|||
"silent_payments_scanned_tip": "Сканиран за съвет! (${tip})",
|
||||
"silent_payments_scanning": "Безшумни плащания за сканиране",
|
||||
"silent_payments_settings": "Настройки за безшумни плащания",
|
||||
"single_seed_wallets_group": "Портфейли с единични семена",
|
||||
"slidable": "Плъзгащ се",
|
||||
"sort_by": "Сортирай по",
|
||||
"spend_key_private": "Spend key (таен)",
|
||||
|
@ -718,12 +728,13 @@
|
|||
"switchToEVMCompatibleWallet": "Моля, превключете към портфейл, съвместим с EVM, и опитайте отново (Ethereum, Polygon)",
|
||||
"symbol": "Символ",
|
||||
"sync_all_wallets": "Синхронизирайте всички портфейли",
|
||||
"sync_status_attempting_scan": "Опит за сканиране",
|
||||
"sync_status_attempting_sync": "ОПИТ ЗА СИНХРОНИЗАЦИЯ",
|
||||
"sync_status_connected": "СВЪРЗВАНЕ",
|
||||
"sync_status_connecting": "СВЪРЗВАНЕ",
|
||||
"sync_status_failed_connect": "НЕУСПЕШНО СВЪРЗВАНЕ",
|
||||
"sync_status_not_connected": "НЯМА ВРЪЗКА",
|
||||
"sync_status_starting_scan": "Стартово сканиране",
|
||||
"sync_status_starting_scan": "Стартиране на сканиране (от ${height})",
|
||||
"sync_status_starting_sync": "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ",
|
||||
"sync_status_syncronized": "СИНХРОНИЗИРАНО",
|
||||
"sync_status_syncronizing": "СИНХРОНИЗИРАНЕ",
|
||||
|
@ -855,8 +866,16 @@
|
|||
"view_transaction_on": "Вижте транзакция на ",
|
||||
"voting_weight": "Тегло на гласуване",
|
||||
"waitFewSecondForTxUpdate": "Моля, изчакайте няколко секунди, докато транзакцията се отрази в историята на транзакциите",
|
||||
"wallet_group": "Група на портфейла",
|
||||
"wallet_group_description_four": "За да създадете портфейл с изцяло ново семе.",
|
||||
"wallet_group_description_one": "В портфейла за торта можете да създадете a",
|
||||
"wallet_group_description_three": "За да видите наличния екран за портфейли и/или групи за портфейли. Или изберете",
|
||||
"wallet_group_description_two": "Чрез избора на съществуващ портфейл, с който да споделите семе. Всяка група за портфейл може да съдържа по един портфейл от всеки тип валута. \n\n Можете да изберете",
|
||||
"wallet_group_empty_state_text_one": "Изглежда, че нямате съвместими групи портфейли !\n\n tap",
|
||||
"wallet_group_empty_state_text_two": "по -долу, за да се направи нов.",
|
||||
"wallet_keys": "Seed/keys на портфейла",
|
||||
"wallet_list_create_new_wallet": "Създаване на нов портфейл",
|
||||
"wallet_list_edit_group_name": "Редактиране на име на групата",
|
||||
"wallet_list_edit_wallet": "Редактиране на портфейла",
|
||||
"wallet_list_failed_to_load": "Грешка при зареждането на портфейл ${wallet_name}. ${error}",
|
||||
"wallet_list_failed_to_remove": "Грешка при премахването на портфейл${wallet_name}. ${error}",
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"choose_one": "Zvolte si",
|
||||
"choose_relay": "Vyberte relé, které chcete použít",
|
||||
"choose_wallet_currency": "Prosím zvolte si měnu pro peněženku:",
|
||||
"choose_wallet_group": "Vyberte skupinu peněženky",
|
||||
"clear": "Smazat",
|
||||
"clearnet_link": "Odkaz na Clearnet",
|
||||
"close": "zavřít",
|
||||
|
@ -176,6 +177,7 @@
|
|||
"create_invoice": "Vytvořit fakturu",
|
||||
"create_new": "Vytvořit novou peněženku",
|
||||
"create_new_account": "Vytvořit nový účet",
|
||||
"create_new_seed": "Vytvořte nové semeno",
|
||||
"creating_new_wallet": "Vytvářím novou peněženku",
|
||||
"creating_new_wallet_error": "Chyba: ${description}",
|
||||
"creation_date": "Datum vzniku",
|
||||
|
@ -232,8 +234,9 @@
|
|||
"edit_token": "Upravit token",
|
||||
"electrum_address_disclaimer": "Po každém použití je generována nová adresa, ale předchozí adresy také stále fungují",
|
||||
"email_address": "E-mailová adresa",
|
||||
"enable_mempool_api": "Mempool API pro přesné poplatky a data",
|
||||
"enable_replace_by_fee": "Povolit výměnu podle poplatku",
|
||||
"enable_silent_payments_scanning": "Povolte skenování tichých plateb",
|
||||
"enable_silent_payments_scanning": "Začněte skenovat tiché platby, dokud není dosaženo špičky",
|
||||
"enabled": "Povoleno",
|
||||
"enter_amount": "Zadejte částku",
|
||||
"enter_backup_password": "Zde zadejte své heslo pro zálohy",
|
||||
|
@ -295,6 +298,7 @@
|
|||
"failed_authentication": "Ověřování selhalo. ${state_error}",
|
||||
"faq": "FAQ",
|
||||
"features": "Funkce",
|
||||
"fee_rate": "Sazba poplatků",
|
||||
"fetching": "Načítá se",
|
||||
"fiat_api": "Fiat API",
|
||||
"fiat_balance": "Fiat Balance",
|
||||
|
@ -608,6 +612,8 @@
|
|||
"seed_share": "Sdílet seed",
|
||||
"seed_title": "Seed",
|
||||
"seedtype": "SeedType",
|
||||
"seedtype_alert_content": "Sdílení semen s jinými peněženkami je možné pouze u BIP39 SeedType.",
|
||||
"seedtype_alert_title": "Upozornění seedtype",
|
||||
"seedtype_legacy": "Legacy (25 slov)",
|
||||
"seedtype_polyseed": "Polyseed (16 slov)",
|
||||
"select_backup_file": "Vybrat soubor se zálohou",
|
||||
|
@ -620,6 +626,7 @@
|
|||
"send": "Poslat",
|
||||
"send_address": "${cryptoCurrency} adresa",
|
||||
"send_amount": "Částka:",
|
||||
"send_change_to_you": "Změňte, vám:",
|
||||
"send_creating_transaction": "Vytváření transakce",
|
||||
"send_error_currency": "Měna může obsahovat pouze čísla",
|
||||
"send_error_minimum_value": "Minimální částka je 0,01",
|
||||
|
@ -673,6 +680,7 @@
|
|||
"setup_your_debit_card": "Nastavit debetní kartu",
|
||||
"share": "Podíl",
|
||||
"share_address": "Sdílet adresu",
|
||||
"shared_seed_wallet_groups": "Skupiny sdílených semen",
|
||||
"show_details": "Zobrazit detaily",
|
||||
"show_keys": "Zobrazit seed/klíče",
|
||||
"show_market_place": "Zobrazit trh",
|
||||
|
@ -686,6 +694,7 @@
|
|||
"signature_invalid_error": "Podpis není platný pro danou zprávu",
|
||||
"signTransaction": "Podepsat transakci",
|
||||
"signup_for_card_accept_terms": "Zaregistrujte se pro kartu a souhlaste s podmínkami.",
|
||||
"silent_payment": "Tichá platba",
|
||||
"silent_payments": "Tiché platby",
|
||||
"silent_payments_always_scan": "Nastavit tiché platby vždy skenování",
|
||||
"silent_payments_disclaimer": "Nové adresy nejsou nové identity. Je to opětovné použití existující identity s jiným štítkem.",
|
||||
|
@ -696,6 +705,7 @@
|
|||
"silent_payments_scanned_tip": "Naskenované na tip! (${tip})",
|
||||
"silent_payments_scanning": "Skenování tichých plateb",
|
||||
"silent_payments_settings": "Nastavení tichých plateb",
|
||||
"single_seed_wallets_group": "Jednorázové peněženky",
|
||||
"slidable": "Posuvné",
|
||||
"sort_by": "Seřazeno podle",
|
||||
"spend_key_private": "Klíč pro platby (soukromý)",
|
||||
|
@ -718,12 +728,13 @@
|
|||
"switchToEVMCompatibleWallet": "Přepněte na peněženku kompatibilní s EVM a zkuste to znovu (Ethereum, Polygon)",
|
||||
"symbol": "Symbol",
|
||||
"sync_all_wallets": "Synchronizovat všechny peněženky",
|
||||
"sync_status_attempting_scan": "Pokus o skenování",
|
||||
"sync_status_attempting_sync": "ZAHAJUJI SYNCHR.",
|
||||
"sync_status_connected": "PŘIPOJENO",
|
||||
"sync_status_connecting": "PŘIPOJOVÁNÍ",
|
||||
"sync_status_failed_connect": "ODPOJENO",
|
||||
"sync_status_not_connected": "NEPŘIPOJENO",
|
||||
"sync_status_starting_scan": "Počáteční skenování",
|
||||
"sync_status_starting_scan": "Počáteční skenování (z ${height})",
|
||||
"sync_status_starting_sync": "SPOUŠTĚNÍ SYNCHRONIZACE",
|
||||
"sync_status_syncronized": "SYNCHRONIZOVÁNO",
|
||||
"sync_status_syncronizing": "SYNCHRONIZUJI",
|
||||
|
@ -855,8 +866,16 @@
|
|||
"view_transaction_on": "Zobrazit transakci na ",
|
||||
"voting_weight": "Hlasová váha",
|
||||
"waitFewSecondForTxUpdate": "Počkejte několik sekund, než se transakce projeví v historii transakcí",
|
||||
"wallet_group": "Skupina peněženky",
|
||||
"wallet_group_description_four": "Vytvoření peněženky s zcela novým semenem.",
|
||||
"wallet_group_description_one": "V peněžence dortu můžete vytvořit a",
|
||||
"wallet_group_description_three": "Chcete -li zobrazit dostupnou obrazovku Skupina skupin peněženek a/nebo skupin peněženek. Nebo zvolit",
|
||||
"wallet_group_description_two": "Výběrem existující peněženky pro sdílení semeno. Každá skupina peněženek může obsahovat jednu peněženku každého typu měny. \n\n Můžete si vybrat",
|
||||
"wallet_group_empty_state_text_one": "Vypadá to, že nemáte žádné kompatibilní skupiny peněženky !\n\n",
|
||||
"wallet_group_empty_state_text_two": "Níže vytvořit nový.",
|
||||
"wallet_keys": "Seed/klíče peněženky",
|
||||
"wallet_list_create_new_wallet": "Vytvořit novou peněženku",
|
||||
"wallet_list_edit_group_name": "Upravit název skupiny",
|
||||
"wallet_list_edit_wallet": "Upravit peněženku",
|
||||
"wallet_list_failed_to_load": "Chyba při načítání ${wallet_name} peněženky. ${error}",
|
||||
"wallet_list_failed_to_remove": "Chyba při odstraňování ${wallet_name} peněženky. ${error}",
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"choose_one": "Wähle ein",
|
||||
"choose_relay": "Bitte wählen Sie ein zu verwendendes Relais aus",
|
||||
"choose_wallet_currency": "Bitte wählen Sie die Währung der Wallet:",
|
||||
"choose_wallet_group": "Wählen Sie Brieftaschengruppe",
|
||||
"clear": "Zurücksetzen",
|
||||
"clearnet_link": "Clearnet-Link",
|
||||
"close": "Schließen",
|
||||
|
@ -176,6 +177,7 @@
|
|||
"create_invoice": "Rechnung erstellen",
|
||||
"create_new": "Neue Wallet erstellen",
|
||||
"create_new_account": "Neues Konto erstellen",
|
||||
"create_new_seed": "Neue Samen erstellen",
|
||||
"creating_new_wallet": "Neue Wallet erstellen",
|
||||
"creating_new_wallet_error": "Fehler: ${description}",
|
||||
"creation_date": "Erstellungsdatum",
|
||||
|
@ -232,8 +234,9 @@
|
|||
"edit_token": "Token bearbeiten",
|
||||
"electrum_address_disclaimer": "Wir generieren jedes Mal neue Adressen, wenn Sie eine verwenden, aber vorherige Adressen funktionieren weiterhin",
|
||||
"email_address": "E-Mail-Adresse",
|
||||
"enable_mempool_api": "Mempool -API für genaue Gebühren und Daten",
|
||||
"enable_replace_by_fee": "Aktivieren Sie Ersatz für Fee",
|
||||
"enable_silent_payments_scanning": "Aktivieren Sie stille Zahlungen Scannen",
|
||||
"enable_silent_payments_scanning": "Scannen Sie stille Zahlungen, bis die Spitze erreicht ist",
|
||||
"enabled": "Ermöglicht",
|
||||
"enter_amount": "Betrag eingeben",
|
||||
"enter_backup_password": "Sicherungskennwort hier eingeben",
|
||||
|
@ -295,6 +298,7 @@
|
|||
"failed_authentication": "Authentifizierung fehlgeschlagen. ${state_error}",
|
||||
"faq": "Häufig gestellte Fragen",
|
||||
"features": "Merkmale",
|
||||
"fee_rate": "Gebührenpreis",
|
||||
"fetching": "Frage ab",
|
||||
"fiat_api": "Fiat API",
|
||||
"fiat_balance": "Fiat Balance",
|
||||
|
@ -609,6 +613,8 @@
|
|||
"seed_share": "Seed teilen",
|
||||
"seed_title": "Seed",
|
||||
"seedtype": "Seedtyp",
|
||||
"seedtype_alert_content": "Das Teilen von Samen mit anderen Brieftaschen ist nur mit bip39 Seedype möglich.",
|
||||
"seedtype_alert_title": "Seedype -Alarm",
|
||||
"seedtype_legacy": "Veraltet (25 Wörter)",
|
||||
"seedtype_polyseed": "Polyseed (16 Wörter)",
|
||||
"select_backup_file": "Sicherungsdatei auswählen",
|
||||
|
@ -621,6 +627,7 @@
|
|||
"send": "Senden",
|
||||
"send_address": "${cryptoCurrency}-Adresse",
|
||||
"send_amount": "Betrag:",
|
||||
"send_change_to_you": "Verändere dich zu dir:",
|
||||
"send_creating_transaction": "Erstelle Transaktion",
|
||||
"send_error_currency": "Die Währung darf nur Zahlen enthalten",
|
||||
"send_error_minimum_value": "Der Mindestbetrag ist 0,01",
|
||||
|
@ -674,6 +681,7 @@
|
|||
"setup_your_debit_card": "Richten Sie Ihre Debitkarte ein",
|
||||
"share": "Teilen",
|
||||
"share_address": "Adresse teilen ",
|
||||
"shared_seed_wallet_groups": "Gemeinsame Samenbrieftaschengruppen",
|
||||
"show_details": "Details anzeigen",
|
||||
"show_keys": "Seed/Schlüssel anzeigen",
|
||||
"show_market_place": "Marktplatz anzeigen",
|
||||
|
@ -687,6 +695,7 @@
|
|||
"signature_invalid_error": "Die Signatur gilt nicht für die angegebene Nachricht",
|
||||
"signTransaction": "Transaktion unterzeichnen",
|
||||
"signup_for_card_accept_terms": "Melden Sie sich für die Karte an und akzeptieren Sie die Bedingungen.",
|
||||
"silent_payment": "Stille Zahlung",
|
||||
"silent_payments": "Stille Zahlungen",
|
||||
"silent_payments_always_scan": "Setzen Sie stille Zahlungen immer scannen",
|
||||
"silent_payments_disclaimer": "Neue Adressen sind keine neuen Identitäten. Es ist eine Wiederverwendung einer bestehenden Identität mit einem anderen Etikett.",
|
||||
|
@ -697,6 +706,7 @@
|
|||
"silent_payments_scanned_tip": "Gescannt zum Trinkgeld! (${tip})",
|
||||
"silent_payments_scanning": "Stille Zahlungen scannen",
|
||||
"silent_payments_settings": "Einstellungen für stille Zahlungen",
|
||||
"single_seed_wallets_group": "Einzelne Samenbriefen",
|
||||
"slidable": "Verschiebbar",
|
||||
"sort_by": "Sortiere nach",
|
||||
"spend_key_private": "Spend Key (geheim)",
|
||||
|
@ -719,12 +729,13 @@
|
|||
"switchToEVMCompatibleWallet": "Bitte wechseln Sie zu einem EVM-kompatiblen Wallet und versuchen Sie es erneut (Ethereum, Polygon)",
|
||||
"symbol": "Symbol",
|
||||
"sync_all_wallets": "Alle Wallets synchronisieren",
|
||||
"sync_status_attempting_scan": "Versuch Scan",
|
||||
"sync_status_attempting_sync": "SYNC VERSUCHEN",
|
||||
"sync_status_connected": "VERBUNDEN",
|
||||
"sync_status_connecting": "VERBINDEN",
|
||||
"sync_status_failed_connect": "GETRENNT",
|
||||
"sync_status_not_connected": "NICHT VERBUNDEN",
|
||||
"sync_status_starting_scan": "Scan beginnen",
|
||||
"sync_status_starting_scan": "SCAN starten (von ${height})",
|
||||
"sync_status_starting_sync": "STARTE SYNCHRONISIERUNG",
|
||||
"sync_status_syncronized": "SYNCHRONISIERT",
|
||||
"sync_status_syncronizing": "SYNCHRONISIERE",
|
||||
|
@ -858,8 +869,16 @@
|
|||
"voting_weight": "Stimmgewicht",
|
||||
"waitFewSecondForTxUpdate": "Bitte warten Sie einige Sekunden, bis die Transaktion im Transaktionsverlauf angezeigt wird",
|
||||
"waiting_payment_confirmation": "Warte auf Zahlungsbestätigung",
|
||||
"wallet_group": "Brieftaschengruppe",
|
||||
"wallet_group_description_four": "eine Brieftasche mit einem völlig neuen Samen schaffen.",
|
||||
"wallet_group_description_one": "In Kuchenbrieftasche können Sie eine erstellen",
|
||||
"wallet_group_description_three": "Sehen Sie den Bildschirm zur verfügbaren Brieftaschen und/oder Brieftaschengruppen. Oder wählen",
|
||||
"wallet_group_description_two": "Durch die Auswahl einer vorhandenen Brieftasche, mit der ein Samen geteilt werden kann. Jede Brieftaschengruppe kann eine einzelne Brieftasche jedes Währungstyps enthalten. \n\n Sie können auswählen",
|
||||
"wallet_group_empty_state_text_one": "Sieht so aus, als hätten Sie keine kompatiblen Brieftaschengruppen !\n\n TAP",
|
||||
"wallet_group_empty_state_text_two": "unten, um einen neuen zu machen.",
|
||||
"wallet_keys": "Wallet-Seed/-Schlüssel",
|
||||
"wallet_list_create_new_wallet": "Neue Wallet erstellen",
|
||||
"wallet_list_edit_group_name": "Gruppenname bearbeiten",
|
||||
"wallet_list_edit_wallet": "Wallet bearbeiten",
|
||||
"wallet_list_failed_to_load": "Laden der Wallet ${wallet_name} fehlgeschlagen. ${error}",
|
||||
"wallet_list_failed_to_remove": "Fehler beim Entfernen der Wallet ${wallet_name}. ${error}",
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
"choose_one": "Choose one",
|
||||
"choose_relay": "Please choose a relay to use",
|
||||
"choose_wallet_currency": "Please choose wallet currency:",
|
||||
"choose_wallet_group": "Choose Wallet Group",
|
||||
"clear": "Clear",
|
||||
"clearnet_link": "Clearnet link",
|
||||
"close": "Close",
|
||||
|
@ -176,6 +177,7 @@
|
|||
"create_invoice": "Create invoice",
|
||||
"create_new": "Create New Wallet",
|
||||
"create_new_account": "Create new account",
|
||||
"create_new_seed": "Create New Seed",
|
||||
"creating_new_wallet": "Creating new wallet",
|
||||
"creating_new_wallet_error": "Error: ${description}",
|
||||
"creation_date": "Creation Date",
|
||||
|
@ -232,8 +234,9 @@
|
|||
"edit_token": "Edit token",
|
||||
"electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work",
|
||||
"email_address": "Email Address",
|
||||
"enable_mempool_api": "Mempool API for accurate fees and dates",
|
||||
"enable_replace_by_fee": "Enable Replace-By-Fee",
|
||||
"enable_silent_payments_scanning": "Enable silent payments scanning",
|
||||
"enable_silent_payments_scanning": "Start scanning silent payments, until the tip is reached",
|
||||
"enabled": "Enabled",
|
||||
"enter_amount": "Enter Amount",
|
||||
"enter_backup_password": "Enter backup password here",
|
||||
|
@ -295,6 +298,7 @@
|
|||
"failed_authentication": "Failed authentication. ${state_error}",
|
||||
"faq": "FAQ",
|
||||
"features": "Features",
|
||||
"fee_rate": "Fee rate",
|
||||
"fetching": "Fetching",
|
||||
"fiat_api": "Fiat API",
|
||||
"fiat_balance": "Fiat Balance",
|
||||
|
@ -608,6 +612,8 @@
|
|||
"seed_share": "Share seed",
|
||||
"seed_title": "Seed",
|
||||
"seedtype": "Seedtype",
|
||||
"seedtype_alert_content": "Sharing seeds with other wallets is only possible with BIP39 SeedType.",
|
||||
"seedtype_alert_title": "SeedType Alert",
|
||||
"seedtype_legacy": "Legacy (25 words)",
|
||||
"seedtype_polyseed": "Polyseed (16 words)",
|
||||
"seedtype_wownero": "Wownero (14 words)",
|
||||
|
@ -621,6 +627,7 @@
|
|||
"send": "Send",
|
||||
"send_address": "${cryptoCurrency} address",
|
||||
"send_amount": "Amount:",
|
||||
"send_change_to_you": "Change, to you:",
|
||||
"send_creating_transaction": "Creating transaction",
|
||||
"send_error_currency": "Currency can only contain numbers",
|
||||
"send_error_minimum_value": "Minimum value of amount is 0.01",
|
||||
|
@ -674,6 +681,7 @@
|
|||
"setup_your_debit_card": "Set up your debit card",
|
||||
"share": "Share",
|
||||
"share_address": "Share address",
|
||||
"shared_seed_wallet_groups": "Shared Seed Wallet Groups",
|
||||
"show_details": "Show Details",
|
||||
"show_keys": "Show seed/keys",
|
||||
"show_market_place": "Show Marketplace",
|
||||
|
@ -687,6 +695,7 @@
|
|||
"signature_invalid_error": "The signature is not valid for the message given",
|
||||
"signTransaction": "Sign Transaction",
|
||||
"signup_for_card_accept_terms": "Sign up for the card and accept the terms.",
|
||||
"silent_payment": "Silent Payment",
|
||||
"silent_payments": "Silent Payments",
|
||||
"silent_payments_always_scan": "Set Silent Payments always scanning",
|
||||
"silent_payments_disclaimer": "New addresses are not new identities. It is a re-use of an existing identity with a different label.",
|
||||
|
@ -697,6 +706,7 @@
|
|||
"silent_payments_scanned_tip": "SCANNED TO TIP! (${tip})",
|
||||
"silent_payments_scanning": "Silent Payments Scanning",
|
||||
"silent_payments_settings": "Silent Payments settings",
|
||||
"single_seed_wallets_group": "Single Seed Wallets",
|
||||
"slidable": "Slidable",
|
||||
"sort_by": "Sort by",
|
||||
"spend_key_private": "Spend key (private)",
|
||||
|
@ -719,12 +729,13 @@
|
|||
"switchToEVMCompatibleWallet": "Please switch to an EVM compatible wallet and try again (Ethereum, Polygon)",
|
||||
"symbol": "Symbol",
|
||||
"sync_all_wallets": "Sync all wallets",
|
||||
"sync_status_attempting_scan": "ATTEMPTING SCAN",
|
||||
"sync_status_attempting_sync": "ATTEMPTING SYNC",
|
||||
"sync_status_connected": "CONNECTED",
|
||||
"sync_status_connecting": "CONNECTING",
|
||||
"sync_status_failed_connect": "DISCONNECTED",
|
||||
"sync_status_not_connected": "NOT CONNECTED",
|
||||
"sync_status_starting_scan": "STARTING SCAN",
|
||||
"sync_status_starting_scan": "STARTING SCAN (from ${height})",
|
||||
"sync_status_starting_sync": "STARTING SYNC",
|
||||
"sync_status_syncronized": "SYNCHRONIZED",
|
||||
"sync_status_syncronizing": "SYNCHRONIZING",
|
||||
|
@ -856,8 +867,16 @@
|
|||
"view_transaction_on": "View Transaction on ",
|
||||
"voting_weight": "Voting Weight",
|
||||
"waitFewSecondForTxUpdate": "Kindly wait for a few seconds for transaction to reflect in transactions history",
|
||||
"wallet_group": "Wallet Group",
|
||||
"wallet_group_description_four": "to create a wallet with an entirely new seed.",
|
||||
"wallet_group_description_one": "In Cake Wallet, you can create a",
|
||||
"wallet_group_description_three": "to see the available wallets and/or wallet groups screen. Or choose",
|
||||
"wallet_group_description_two": "by selecting an existing wallet to share a seed with. Each wallet group can contain a single wallet of each currency type.\n\nYou can select",
|
||||
"wallet_group_empty_state_text_one": "Looks like you don't have any compatible wallet groups!\n\nTap",
|
||||
"wallet_group_empty_state_text_two": "below to make a new one.",
|
||||
"wallet_keys": "Wallet seed/keys",
|
||||
"wallet_list_create_new_wallet": "Create New Wallet",
|
||||
"wallet_list_edit_group_name": "Edit Group Name",
|
||||
"wallet_list_edit_wallet": "Edit wallet",
|
||||
"wallet_list_failed_to_load": "Failed to load ${wallet_name} wallet. ${error}",
|
||||
"wallet_list_failed_to_remove": "Failed to remove ${wallet_name} wallet. ${error}",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue