mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-04-27 14:04:46 +00:00
feat: misc reviews
This commit is contained in:
parent
7da30d85b7
commit
309dca9ac9
29 changed files with 568 additions and 338 deletions
cw_bitcoin/lib
bitcoin_address_record.dartbitcoin_unspent.dartbitcoin_wallet.dartbitcoin_wallet_addresses.dartbitcoin_wallet_snapshot.dartelectrum_transaction_info.dartelectrum_wallet.dartelectrum_wallet_addresses.dartelectrum_wallet_snapshot.dart
electrum_worker
litecoin_wallet.dartlitecoin_wallet_addresses.dartlitecoin_wallet_snapshot.dartcw_bitcoin_cash/lib/src
lib
bitcoin
entities
src/screens
view_model
|
@ -4,6 +4,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
|||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
abstract class BaseBitcoinAddressRecord {
|
||||
BaseBitcoinAddressRecord(
|
||||
|
@ -20,16 +21,15 @@ abstract class BaseBitcoinAddressRecord {
|
|||
_balance = balance,
|
||||
_name = name,
|
||||
_isUsed = isUsed,
|
||||
_isHidden = isHidden ?? isChange,
|
||||
isHidden = isHidden ?? isChange,
|
||||
_isChange = isChange;
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address;
|
||||
|
||||
final String address;
|
||||
bool _isHidden;
|
||||
bool isHidden;
|
||||
|
||||
bool get isHidden => _isHidden;
|
||||
final bool _isChange;
|
||||
|
||||
bool get isChange => _isChange;
|
||||
|
@ -53,7 +53,7 @@ abstract class BaseBitcoinAddressRecord {
|
|||
|
||||
void setAsUsed() {
|
||||
_isUsed = true;
|
||||
_isHidden = true;
|
||||
isHidden = true;
|
||||
}
|
||||
|
||||
void setNewName(String label) => _name = label;
|
||||
|
@ -75,11 +75,15 @@ abstract class BaseBitcoinAddressRecord {
|
|||
'runtimeType': runtimeType.toString(),
|
||||
});
|
||||
|
||||
static BaseBitcoinAddressRecord fromJSON(String jsonSource) {
|
||||
static BaseBitcoinAddressRecord fromJSON(
|
||||
String jsonSource, [
|
||||
DerivationInfo? derivationInfo,
|
||||
BasedUtxoNetwork? network,
|
||||
]) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
if (decoded['runtimeType'] == 'BitcoinAddressRecord') {
|
||||
return BitcoinAddressRecord.fromJSON(jsonSource);
|
||||
return BitcoinAddressRecord.fromJSON(jsonSource, derivationInfo, network);
|
||||
} else if (decoded['runtimeType'] == 'BitcoinSilentPaymentAddressRecord') {
|
||||
return BitcoinSilentPaymentAddressRecord.fromJSON(jsonSource);
|
||||
} else if (decoded['runtimeType'] == 'BitcoinReceivedSPAddressRecord') {
|
||||
|
@ -87,7 +91,7 @@ abstract class BaseBitcoinAddressRecord {
|
|||
} else if (decoded['runtimeType'] == 'LitecoinMWEBAddressRecord') {
|
||||
return LitecoinMWEBAddressRecord.fromJSON(jsonSource);
|
||||
} else {
|
||||
throw ArgumentError('Unknown runtimeType');
|
||||
return BitcoinAddressRecord.fromJSON(jsonSource, derivationInfo, network);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,17 +124,34 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
}
|
||||
}
|
||||
|
||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
||||
factory BitcoinAddressRecord.fromJSON(
|
||||
String jsonSource, [
|
||||
DerivationInfo? derivationInfo,
|
||||
BasedUtxoNetwork? network,
|
||||
]) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
final derivationInfoSnp = decoded['derivationInfo'] as Map<String, dynamic>?;
|
||||
final derivationTypeSnp = decoded['derivationType'] as int?;
|
||||
final cwDerivationType = derivationTypeSnp != null
|
||||
? CWBitcoinDerivationType.values[derivationTypeSnp]
|
||||
: derivationInfo!.derivationType == DerivationType.bip39
|
||||
? CWBitcoinDerivationType.old_bip39
|
||||
: CWBitcoinDerivationType.old_electrum;
|
||||
|
||||
return BitcoinAddressRecord(
|
||||
decoded['address'] as String,
|
||||
index: decoded['index'] as int,
|
||||
derivationInfo: BitcoinDerivationInfo.fromJSON(
|
||||
decoded['derivationInfo'] as Map<String, dynamic>,
|
||||
),
|
||||
// TODO: make nullable maybe?
|
||||
cwDerivationType: CWBitcoinDerivationType.values[decoded['derivationType'] as int],
|
||||
derivationInfo: derivationInfoSnp == null
|
||||
? [CWBitcoinDerivationType.bip39, CWBitcoinDerivationType.old_bip39]
|
||||
.contains(cwDerivationType)
|
||||
? BitcoinDerivationInfo.fromDerivationAndAddress(
|
||||
BitcoinDerivationType.bip39,
|
||||
decoded['address'] as String,
|
||||
network!,
|
||||
)
|
||||
: BitcoinDerivationInfos.ELECTRUM
|
||||
: BitcoinDerivationInfo.fromJSON(derivationInfoSnp),
|
||||
cwDerivationType: cwDerivationType,
|
||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||
isChange: decoded['isChange'] as bool? ?? false,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||
|
@ -218,7 +239,7 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
return BitcoinSilentPaymentAddressRecord(
|
||||
decoded['address'] as String,
|
||||
derivationPath:
|
||||
decoded['derivationPath'] as String? ?? BitcoinWalletAddressesBase.OLD_SP_SPEND_PATH,
|
||||
(decoded['derivationPath'] as String?) ?? BitcoinWalletAddressesBase.OLD_SP_PATH,
|
||||
labelIndex: decoded['index'] as int,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||
txCount: decoded['txCount'] as int? ?? 0,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_core/unspent_transaction_output.dart';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class BitcoinUnspent extends Unspent {
|
||||
BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout)
|
||||
|
@ -10,9 +11,19 @@ class BitcoinUnspent extends Unspent {
|
|||
factory BitcoinUnspent.fromUTXO(BaseBitcoinAddressRecord address, ElectrumUtxo utxo) =>
|
||||
BitcoinUnspent(address, utxo.txId, utxo.value.toInt(), utxo.vout);
|
||||
|
||||
factory BitcoinUnspent.fromJSON(BaseBitcoinAddressRecord? address, Map<String, dynamic> json) {
|
||||
factory BitcoinUnspent.fromJSON(
|
||||
BaseBitcoinAddressRecord? address,
|
||||
Map<String, dynamic> json, [
|
||||
DerivationInfo? derivationInfo,
|
||||
BasedUtxoNetwork? network,
|
||||
]) {
|
||||
return BitcoinUnspent(
|
||||
address ?? BaseBitcoinAddressRecord.fromJSON(json['address_record'] as String),
|
||||
address ??
|
||||
BaseBitcoinAddressRecord.fromJSON(
|
||||
json['address_record'] as String,
|
||||
derivationInfo,
|
||||
network,
|
||||
),
|
||||
json['tx_hash'] as String,
|
||||
int.parse(json['value'].toString()),
|
||||
int.parse(json['tx_pos'].toString()),
|
||||
|
|
|
@ -144,7 +144,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
snp = await BitcoinWalletSnapshot.load(
|
||||
encryptionFileUtils,
|
||||
name,
|
||||
walletInfo.type,
|
||||
walletInfo,
|
||||
password,
|
||||
network,
|
||||
);
|
||||
|
@ -209,27 +209,29 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
final isNamedElectrs = node?.uri.host.contains("electrs") ?? false;
|
||||
if (isNamedElectrs) {
|
||||
node!.isElectrs = true;
|
||||
node!.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
final isNamedFulcrum = node!.uri.host.contains("fulcrum");
|
||||
if (isNamedFulcrum) {
|
||||
node!.isElectrs = false;
|
||||
node!.save();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node!.isElectrs == null) {
|
||||
final version = await waitSendWorker(ElectrumWorkerGetVersionRequest());
|
||||
final version = await waitSendWorker(ElectrumWorkerGetVersionRequest());
|
||||
|
||||
if (version is List<String> && version.isNotEmpty) {
|
||||
final server = version[0];
|
||||
if (version is List<String> && version.isNotEmpty) {
|
||||
final server = version[0];
|
||||
|
||||
if (server.toLowerCase().contains('electrs')) {
|
||||
node!.isElectrs = true;
|
||||
}
|
||||
} else if (version is String && version.toLowerCase().contains('electrs')) {
|
||||
if (server.toLowerCase().contains('electrs')) {
|
||||
node!.isElectrs = true;
|
||||
} else {
|
||||
node!.isElectrs = false;
|
||||
}
|
||||
} else if (version is String && version.toLowerCase().contains('electrs')) {
|
||||
node!.isElectrs = true;
|
||||
} else {
|
||||
node!.isElectrs = false;
|
||||
}
|
||||
|
||||
node!.save();
|
||||
|
@ -293,8 +295,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
||||
for (final utxo in utxos) {
|
||||
final rawTx =
|
||||
(await getTransactionExpanded(hash: utxo.utxo.txHash)).originalTransaction.toHex();
|
||||
final rawTx = await getTransactionHex(hash: utxo.utxo.txHash);
|
||||
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||
|
||||
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
||||
|
@ -373,7 +374,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> setSilentPaymentsScanning(bool active) async {
|
||||
Future<void> setSilentPaymentsScanning(bool active, [int? height, bool? doSingleScan]) async {
|
||||
silentPaymentsScanningActive = active;
|
||||
final nodeSupportsSilentPayments = await getNodeSupportsSilentPayments();
|
||||
final isAllowedToScan = nodeSupportsSilentPayments || allowedToSwitchNodesForScanning;
|
||||
|
@ -382,14 +383,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
syncStatus = AttemptingScanSyncStatus();
|
||||
|
||||
final tip = currentChainTip!;
|
||||
final beginHeight = height ?? walletInfo.restoreHeight;
|
||||
|
||||
if (tip == walletInfo.restoreHeight) {
|
||||
if (tip == beginHeight) {
|
||||
syncStatus = SyncedTipSyncStatus(tip);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tip > walletInfo.restoreHeight) {
|
||||
_setListeners(walletInfo.restoreHeight);
|
||||
if (tip > beginHeight) {
|
||||
_requestTweakScanning(beginHeight, doSingleScan: doSingleScan);
|
||||
}
|
||||
} else if (syncStatus is! SyncedSyncStatus) {
|
||||
await waitSendWorker(ElectrumWorkerStopScanningRequest());
|
||||
|
@ -501,23 +503,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
await _setInitialScanHeight();
|
||||
|
||||
await super.startSync();
|
||||
|
||||
if (alwaysScan == true) {
|
||||
_setListeners(walletInfo.restoreHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> rescan({required int height, bool? doSingleScan}) async {
|
||||
silentPaymentsScanningActive = true;
|
||||
_setListeners(height, doSingleScan: doSingleScan);
|
||||
setSilentPaymentsScanning(true, height, doSingleScan);
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -645,14 +634,14 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
final height = result.height;
|
||||
if (height != null) {
|
||||
if (height != null && result.wasSingleBlock == false) {
|
||||
await walletInfo.updateRestoreHeight(height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> _setListeners(int height, {bool? doSingleScan}) async {
|
||||
Future<void> _requestTweakScanning(int height, {bool? doSingleScan}) async {
|
||||
if (currentChainTip == null) {
|
||||
throw Exception("currentChainTip is null");
|
||||
}
|
||||
|
@ -763,7 +752,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
// New headers received, start scanning
|
||||
if (alwaysScan == true && syncStatus is SyncedSyncStatus) {
|
||||
_setListeners(walletInfo.restoreHeight);
|
||||
_requestTweakScanning(walletInfo.restoreHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
silentPaymentWallets = [silentPaymentWallet!];
|
||||
}
|
||||
|
||||
static const OLD_SP_SPEND_PATH = "m/352'/1'/0'/0'/0";
|
||||
static const OLD_SP_PATH = "m/352'/1'/0'/#'/0";
|
||||
static const BITCOIN_ADDRESS_TYPES = [
|
||||
SegwitAddressType.p2wpkh,
|
||||
P2pkhAddressType.p2pkh,
|
||||
|
@ -74,8 +74,8 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
|
||||
if (silentPaymentAddresses.isEmpty) {
|
||||
if (walletInfo.isRecovery) {
|
||||
final oldScanPath = Bip32PathParser.parse("m/352'/1'/0'/1'/0");
|
||||
final oldSpendPath = Bip32PathParser.parse("m/352'/1'/0'/0'/0");
|
||||
final oldScanPath = Bip32PathParser.parse(OLD_SP_PATH.replaceFirst("#", "1"));
|
||||
final oldSpendPath = Bip32PathParser.parse(OLD_SP_PATH.replaceFirst("#", "0"));
|
||||
|
||||
final oldSilentPaymentWallet = SilentPaymentOwner.fromPrivateKeys(
|
||||
b_scan: ECPrivate(hdWallet.derive(oldScanPath).privateKey),
|
||||
|
@ -315,7 +315,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
final usableSilentPaymentAddresses = silentPaymentAddresses
|
||||
.where((a) =>
|
||||
a.type != SegwitAddressType.p2tr &&
|
||||
a.derivationPath != OLD_SP_SPEND_PATH &&
|
||||
a.derivationPath != OLD_SP_PATH &&
|
||||
a.isChange == false)
|
||||
.toList();
|
||||
final nextSPLabelIndex = usableSilentPaymentAddresses.length;
|
||||
|
@ -330,7 +330,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
);
|
||||
|
||||
silentPaymentAddresses.add(address);
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateAddressesByType();
|
||||
|
||||
return address;
|
||||
}
|
||||
|
@ -382,14 +382,17 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
|
||||
@override
|
||||
@action
|
||||
void updateAddressesOnReceiveScreen() {
|
||||
void updateAddressesByType() {
|
||||
if (addressPageType == SilentPaymentsAddresType.p2sp) {
|
||||
addressesOnReceiveScreen.clear();
|
||||
addressesOnReceiveScreen.addAll(silentPaymentAddresses);
|
||||
receiveAddressesByType.clear();
|
||||
receiveAddressesByType[SilentPaymentsAddresType.p2sp] = silentPaymentAddresses
|
||||
.where((addressRecord) =>
|
||||
addressRecord.type == SilentPaymentsAddresType.p2sp && !addressRecord.isChange)
|
||||
.toList();
|
||||
return;
|
||||
}
|
||||
|
||||
super.updateAddressesOnReceiveScreen();
|
||||
super.updateAddressesByType();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -398,7 +401,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
addressesSet.addAll(addresses);
|
||||
this.silentPaymentAddresses.clear();
|
||||
this.silentPaymentAddresses.addAll(addressesSet);
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateAddressesByType();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -407,7 +410,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
addressesSet.addAll(addresses);
|
||||
this.receivedSPAddresses.clear();
|
||||
this.receivedSPAddresses.addAll(addressesSet);
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateAddressesByType();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -416,7 +419,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address);
|
||||
|
||||
silentPaymentAddresses.remove(addressRecord);
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateAddressesByType();
|
||||
}
|
||||
|
||||
Map<String, String> get labels {
|
||||
|
@ -492,6 +495,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
required Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets,
|
||||
required BasedUtxoNetwork network,
|
||||
required bool isHardwareWallet,
|
||||
// TODO: make it used
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
|
||||
List<BitcoinReceivedSPAddressRecord>? initialReceivedSPAddresses,
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
|
|||
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class BitcoinWalletSnapshot extends ElectrumWalletSnapshot {
|
||||
BitcoinWalletSnapshot({
|
||||
|
@ -27,10 +27,11 @@ class BitcoinWalletSnapshot extends ElectrumWalletSnapshot {
|
|||
static Future<BitcoinWalletSnapshot> load(
|
||||
EncryptionFileUtils encryptionFileUtils,
|
||||
String name,
|
||||
WalletType type,
|
||||
WalletInfo walletInfo,
|
||||
String password,
|
||||
BasedUtxoNetwork network,
|
||||
) async {
|
||||
final type = walletInfo.type;
|
||||
final path = await pathForWallet(name: name, type: type);
|
||||
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
|
@ -38,7 +39,7 @@ class BitcoinWalletSnapshot extends ElectrumWalletSnapshot {
|
|||
final ElectrumWalletSnapshot electrumWalletSnapshot = await ElectrumWalletSnapshot.load(
|
||||
encryptionFileUtils,
|
||||
name,
|
||||
type,
|
||||
walletInfo,
|
||||
password,
|
||||
network,
|
||||
);
|
||||
|
|
|
@ -160,6 +160,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
List<String> inputAddresses = [];
|
||||
List<String> outputAddresses = [];
|
||||
|
||||
final sentAmounts = <int>[];
|
||||
if (bundle.ins.length > 0) {
|
||||
for (var i = 0; i < bundle.originalTransaction.inputs.length; i++) {
|
||||
final input = bundle.originalTransaction.inputs[i];
|
||||
|
@ -167,11 +168,13 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
final outTransaction = inputTransaction.outputs[input.txIndex];
|
||||
inputAmount += outTransaction.amount.toInt();
|
||||
if (addresses.contains(
|
||||
BitcoinAddressUtils.addressFromOutputScript(outTransaction.scriptPubKey, network))) {
|
||||
BitcoinAddressUtils.addressFromOutputScript(outTransaction.scriptPubKey, network),
|
||||
)) {
|
||||
direction = TransactionDirection.outgoing;
|
||||
inputAddresses.add(
|
||||
BitcoinAddressUtils.addressFromOutputScript(outTransaction.scriptPubKey, network),
|
||||
);
|
||||
sentAmounts.add(outTransaction.amount.toInt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,6 +217,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
// Self-send
|
||||
direction = TransactionDirection.incoming;
|
||||
amount = receivedAmounts.reduce((a, b) => a + b);
|
||||
} else if (sentAmounts.length > 0) {
|
||||
amount = sentAmounts.reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
final fee = inputAmount - totalOutAmount;
|
||||
|
|
|
@ -186,8 +186,7 @@ abstract class ElectrumWalletBase
|
|||
onBalanceResponse(response.result);
|
||||
break;
|
||||
case ElectrumRequestMethods.getHistoryMethod:
|
||||
final response = ElectrumWorkerGetHistoryResponse.fromJson(messageJson);
|
||||
onHistoriesResponse(response.result);
|
||||
onHistoriesResponse(ElectrumWorkerGetHistoryResponse.fromJson(messageJson));
|
||||
break;
|
||||
case ElectrumRequestMethods.listunspentMethod:
|
||||
final response = ElectrumWorkerListUnspentResponse.fromJson(messageJson);
|
||||
|
@ -1270,12 +1269,10 @@ abstract class ElectrumWalletBase
|
|||
await Future.forEach(walletAddresses.allAddresses, (BitcoinAddressRecord addressRecord) async {
|
||||
final isChange = addressRecord.isChange;
|
||||
|
||||
final matchingAddressList =
|
||||
(isChange ? walletAddresses.changeAddresses : walletAddresses.receiveAddresses).where(
|
||||
(element) =>
|
||||
element.type == addressRecord.type &&
|
||||
element.cwDerivationType == addressRecord.cwDerivationType,
|
||||
);
|
||||
final matchingAddressList = walletAddresses
|
||||
.getAddressesByType(addressRecord.type, isChange)
|
||||
.where((element) =>
|
||||
(element as BitcoinAddressRecord).cwDerivationType == addressRecord.cwDerivationType);
|
||||
final totalMatchingAddresses = matchingAddressList.length;
|
||||
|
||||
final matchingGapLimit = (isChange
|
||||
|
@ -1305,11 +1302,11 @@ abstract class ElectrumWalletBase
|
|||
walletAddresses.updateAdresses(newAddresses);
|
||||
|
||||
final newMatchingAddressList =
|
||||
(isChange ? walletAddresses.changeAddresses : walletAddresses.receiveAddresses).where(
|
||||
(element) =>
|
||||
element.type == addressRecord.type &&
|
||||
element.cwDerivationType == addressRecord.cwDerivationType,
|
||||
);
|
||||
walletAddresses.getAddressesByType(addressRecord.type, isChange).where(
|
||||
(element) =>
|
||||
element.type == addressRecord.type &&
|
||||
(element as BitcoinAddressRecord) == addressRecord.cwDerivationType,
|
||||
);
|
||||
printV(
|
||||
"discovered ${newAddresses.length} new ${isChange ? "change" : "receive"} addresses");
|
||||
printV(
|
||||
|
@ -1329,7 +1326,8 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
@action
|
||||
Future<void> onHistoriesResponse(List<AddressHistoriesResponse> histories) async {
|
||||
Future<void> onHistoriesResponse(ElectrumWorkerGetHistoryResponse response) async {
|
||||
final histories = response.result;
|
||||
if (histories.isNotEmpty) {
|
||||
final addressesWithHistory = <BitcoinAddressRecord>[];
|
||||
|
||||
|
@ -1350,8 +1348,8 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
await save();
|
||||
} else {
|
||||
// checkAddressesGap();
|
||||
} else if (response.completed) {
|
||||
checkAddressesGap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1582,7 +1580,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
// Identify all change outputs
|
||||
final changeAddresses = walletAddresses.changeAddresses;
|
||||
final changeAddresses = walletAddresses.allChangeAddresses;
|
||||
final List<BitcoinOutput> changeOutputs = outputs
|
||||
.where((output) => changeAddresses
|
||||
.any((element) => element.address == output.address.toAddress(network)))
|
||||
|
@ -1643,6 +1641,12 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<String> getTransactionHex({required String hash}) async {
|
||||
return await waitSendWorker(
|
||||
ElectrumWorkerTxHexRequest(txHash: hash, currentChainTip: currentChainTip!),
|
||||
) as String;
|
||||
}
|
||||
|
||||
Future<ElectrumTransactionBundle> getTransactionExpanded({required String hash}) async {
|
||||
return await waitSendWorker(
|
||||
ElectrumWorkerTxExpandedRequest(txHash: hash, currentChainTip: currentChainTip!),
|
||||
|
|
|
@ -28,12 +28,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
Map<String, int>? initialChangeAddressIndex,
|
||||
BitcoinAddressType? initialAddressPageType,
|
||||
}) : _allAddresses = ObservableList.of(initialAddresses ?? []),
|
||||
addressesOnReceiveScreen =
|
||||
ObservableList<BaseBitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? []).where((addressRecord) => !addressRecord.isChange).toSet()),
|
||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? []).where((addressRecord) => addressRecord.isChange).toSet()),
|
||||
currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {},
|
||||
currentChangeAddressIndexByType = initialChangeAddressIndex ?? {},
|
||||
_addressPageType = initialAddressPageType ??
|
||||
|
@ -46,10 +40,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
static const defaultChangeAddressesCount = 17;
|
||||
static const gap = 20;
|
||||
|
||||
final walletAddressTypes = <BitcoinAddressType>[];
|
||||
|
||||
final ObservableList<BitcoinAddressRecord> _allAddresses;
|
||||
final ObservableList<BaseBitcoinAddressRecord> addressesOnReceiveScreen;
|
||||
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
||||
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
||||
@observable
|
||||
Map<BitcoinAddressType, List<BaseBitcoinAddressRecord>> receiveAddressesByType = {};
|
||||
@observable
|
||||
Map<BitcoinAddressType, List<BaseBitcoinAddressRecord>> changeAddressesByType = {};
|
||||
|
||||
final BasedUtxoNetwork network;
|
||||
|
||||
final Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets;
|
||||
|
@ -61,6 +59,24 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@observable
|
||||
late BitcoinAddressType _addressPageType;
|
||||
|
||||
@computed
|
||||
List<BitcoinAddressRecord> get allChangeAddresses =>
|
||||
_allAddresses.where((addr) => addr.isChange).toList();
|
||||
|
||||
@computed
|
||||
List<BaseBitcoinAddressRecord> get selectedReceiveAddresses =>
|
||||
receiveAddressesByType[_addressPageType]!;
|
||||
|
||||
@computed
|
||||
List<BaseBitcoinAddressRecord> get selectedChangeAddresses =>
|
||||
receiveAddressesByType[_addressPageType]!;
|
||||
|
||||
List<BaseBitcoinAddressRecord> getAddressesByType(
|
||||
BitcoinAddressType type, [
|
||||
bool isChange = false,
|
||||
]) =>
|
||||
isChange ? changeAddressesByType[type]! : receiveAddressesByType[type]!;
|
||||
|
||||
@computed
|
||||
BitcoinAddressType get addressPageType => _addressPageType;
|
||||
|
||||
|
@ -75,6 +91,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return _allAddresses.firstWhere((element) => element.address == address);
|
||||
}
|
||||
|
||||
// TODO: toggle to switch
|
||||
@observable
|
||||
BitcoinAddressType changeAddressType = SegwitAddressType.p2wpkh;
|
||||
|
||||
|
@ -83,9 +100,11 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
String get address {
|
||||
String receiveAddress = "";
|
||||
|
||||
final typeMatchingReceiveAddresses = addressesOnReceiveScreen.where((addr) => !addr.isUsed);
|
||||
final typeMatchingReceiveAddresses = selectedReceiveAddresses.where(
|
||||
(addressRecord) => !addressRecord.isUsed,
|
||||
);
|
||||
|
||||
if ((isEnabledAutoGenerateSubaddress && receiveAddresses.isEmpty) ||
|
||||
if ((isEnabledAutoGenerateSubaddress && selectedReceiveAddresses.isEmpty) ||
|
||||
typeMatchingReceiveAddresses.isEmpty) {
|
||||
receiveAddress = generateNewAddress().address;
|
||||
} else {
|
||||
|
@ -94,7 +113,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
if (typeMatchingReceiveAddresses.isNotEmpty) {
|
||||
if (previousAddressMatchesType &&
|
||||
typeMatchingReceiveAddresses.first.address != addressesOnReceiveScreen.first.address) {
|
||||
typeMatchingReceiveAddresses.first.address != selectedReceiveAddresses.first.address) {
|
||||
receiveAddress = previousAddressRecord!.address;
|
||||
} else {
|
||||
receiveAddress = typeMatchingReceiveAddresses.first.address;
|
||||
|
@ -143,22 +162,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@observable
|
||||
BitcoinAddressRecord? previousAddressRecord;
|
||||
|
||||
@computed
|
||||
int get totalCountOfReceiveAddresses => addressesOnReceiveScreen.fold(0, (acc, addressRecord) {
|
||||
if (!addressRecord.isChange) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
|
||||
@computed
|
||||
int get totalCountOfChangeAddresses => addressesOnReceiveScreen.fold(0, (acc, addressRecord) {
|
||||
if (addressRecord.isChange) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
|
||||
CWBitcoinDerivationType getHDWalletType() {
|
||||
if (hdWallets.containsKey(CWBitcoinDerivationType.bip39)) {
|
||||
return CWBitcoinDerivationType.bip39;
|
||||
|
@ -171,25 +174,21 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateReceiveAddresses();
|
||||
updateChangeAddresses();
|
||||
updateAddressesByType();
|
||||
updateHiddenAddresses();
|
||||
await updateAddressesInBox();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<BaseBitcoinAddressRecord> getChangeAddress() async {
|
||||
updateChangeAddresses();
|
||||
|
||||
final address = changeAddresses.firstWhere(
|
||||
(addressRecord) => _isUnusedChangeAddressByType(addressRecord, changeAddressType),
|
||||
final address = selectedChangeAddresses.firstWhere(
|
||||
(addr) => _isUnusedChangeAddressByType(addr, changeAddressType),
|
||||
);
|
||||
return address;
|
||||
}
|
||||
|
||||
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
|
||||
final newAddressIndex = addressesOnReceiveScreen.fold(
|
||||
final newAddressIndex = selectedReceiveAddresses.fold(
|
||||
0,
|
||||
(int acc, addressRecord) => addressRecord.isChange == false ? acc + 1 : acc,
|
||||
);
|
||||
|
@ -282,21 +281,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
void updateAddressesOnReceiveScreen() {
|
||||
addressesOnReceiveScreen.clear();
|
||||
addressesOnReceiveScreen.addAll(_allAddresses.where(_isAddressPageTypeMatch).toList());
|
||||
}
|
||||
|
||||
@action
|
||||
void updateReceiveAddresses() {
|
||||
receiveAddresses.clear();
|
||||
receiveAddresses.addAll(_allAddresses.where((addressRecord) => !addressRecord.isChange));
|
||||
}
|
||||
|
||||
@action
|
||||
void updateChangeAddresses() {
|
||||
changeAddresses.clear();
|
||||
changeAddresses.addAll(_allAddresses.where((addressRecord) => addressRecord.isChange));
|
||||
void updateAddressesByType() {
|
||||
receiveAddressesByType.clear();
|
||||
walletAddressTypes.forEach((type) {
|
||||
receiveAddressesByType[type] =
|
||||
_allAddresses.where((addr) => _isAddressByType(addr, type)).toList();
|
||||
changeAddressesByType[type] =
|
||||
_allAddresses.where((addr) => _isAddressByType(addr, type)).toList();
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -310,8 +302,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
|
||||
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount;
|
||||
|
||||
final startIndex = (isChange ? changeAddresses : receiveAddresses)
|
||||
.where((addr) => addr.cwDerivationType == derivationType && addr.type == addressType)
|
||||
final startIndex = (isChange ? selectedChangeAddresses : selectedReceiveAddresses)
|
||||
.where((addr) =>
|
||||
(addr as BitcoinAddressRecord).cwDerivationType == derivationType &&
|
||||
addr.type == addressType)
|
||||
.length;
|
||||
|
||||
final newAddresses = <BitcoinAddressRecord>[];
|
||||
|
@ -420,9 +414,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
if (replacedAddresses.isNotEmpty) {
|
||||
_allAddresses.addAll(replacedAddresses);
|
||||
} else {
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateReceiveAddresses();
|
||||
updateChangeAddresses();
|
||||
updateAddressesByType();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,9 +423,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
this._allAddresses.addAll(addresses);
|
||||
|
||||
updateHiddenAddresses();
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateReceiveAddresses();
|
||||
updateChangeAddresses();
|
||||
updateAddressesByType();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -447,18 +437,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@action
|
||||
Future<void> setAddressType(BitcoinAddressType type) async {
|
||||
_addressPageType = type;
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateAddressesByType();
|
||||
walletInfo.addressPageType = addressPageType.toString();
|
||||
await walletInfo.save();
|
||||
}
|
||||
|
||||
bool _isAddressPageTypeMatch(BitcoinAddressRecord addressRecord) {
|
||||
return _isAddressByType(addressRecord, addressPageType);
|
||||
}
|
||||
|
||||
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||
|
||||
bool _isUnusedChangeAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) {
|
||||
bool _isUnusedChangeAddressByType(BaseBitcoinAddressRecord addr, BitcoinAddressType type) {
|
||||
return addr.isChange && !addr.isUsed && addr.type == type;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,10 +48,11 @@ class ElectrumWalletSnapshot {
|
|||
static Future<ElectrumWalletSnapshot> load(
|
||||
EncryptionFileUtils encryptionFileUtils,
|
||||
String name,
|
||||
WalletType type,
|
||||
WalletInfo walletInfo,
|
||||
String password,
|
||||
BasedUtxoNetwork network,
|
||||
) async {
|
||||
final type = walletInfo.type;
|
||||
final path = await pathForWallet(name: name, type: type);
|
||||
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
|
@ -80,8 +81,14 @@ class ElectrumWalletSnapshot {
|
|||
derivationType: derivationType,
|
||||
derivationPath: derivationPath,
|
||||
unspentCoins: (data['unspent_coins'] as List?)
|
||||
?.map((e) => BitcoinUnspent.fromJSON(null, e as Map<String, dynamic>))
|
||||
.toList() ?? [],
|
||||
?.map((e) => BitcoinUnspent.fromJSON(
|
||||
null,
|
||||
e as Map<String, dynamic>,
|
||||
walletInfo.derivationInfo!,
|
||||
network,
|
||||
))
|
||||
.toList() ??
|
||||
[],
|
||||
didInitialSync: data['didInitialSync'] as bool?,
|
||||
walletAddressesSnapshot: walletAddressesSnapshot,
|
||||
);
|
||||
|
|
|
@ -24,6 +24,7 @@ class ElectrumWorker {
|
|||
final SendPort sendPort;
|
||||
ElectrumProvider? _electrumClient;
|
||||
ServerCapability? _serverCapability;
|
||||
String? get version => _serverCapability?.version;
|
||||
BehaviorSubject<Map<String, dynamic>>? _scanningStream;
|
||||
|
||||
BasedUtxoNetwork? _network;
|
||||
|
@ -72,6 +73,11 @@ class ElectrumWorker {
|
|||
ElectrumWorkerTxExpandedRequest.fromJson(messageJson),
|
||||
);
|
||||
break;
|
||||
case ElectrumWorkerMethods.txHexMethod:
|
||||
await _handleGetTxHex(
|
||||
ElectrumWorkerTxHexRequest.fromJson(messageJson),
|
||||
);
|
||||
break;
|
||||
case ElectrumRequestMethods.headersSubscribeMethod:
|
||||
await _handleHeadersSubscribe(
|
||||
ElectrumWorkerHeadersSubscribeRequest.fromJson(messageJson),
|
||||
|
@ -257,6 +263,8 @@ class ElectrumWorker {
|
|||
completed: true,
|
||||
));
|
||||
}
|
||||
}, onError: () {
|
||||
_serverCapability!.supportsBatching = false;
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
|
@ -488,9 +496,7 @@ class ElectrumWorker {
|
|||
}
|
||||
|
||||
if (txVerbose?.isEmpty ?? true) {
|
||||
txHex = await _electrumClient!.request(
|
||||
ElectrumRequestGetTransactionHex(transactionHash: hash),
|
||||
);
|
||||
txHex = await _getTransactionHex(hash: hash);
|
||||
} else {
|
||||
txHex = txVerbose!['hex'] as String;
|
||||
}
|
||||
|
@ -624,11 +630,11 @@ class ElectrumWorker {
|
|||
|
||||
// date is validated when the API responds with the same date at least twice
|
||||
// since sometimes the mempool api returns the wrong date at first
|
||||
final canValidateDate = request.mempoolAPIEnabled || _serverCapability!.supportsTxVerbose;
|
||||
if (tx == null ||
|
||||
tx.original == null ||
|
||||
// TODO: use mempool api or tx verbose
|
||||
// (tx.isDateValidated != true && request.mempoolAPIEnabled)) {
|
||||
(tx.isDateValidated != true)) {
|
||||
(tx.isDateValidated != true && canValidateDate) ||
|
||||
tx.time == null) {
|
||||
transactionsByIds[txid] = TxToFetch(height: height, tx: tx);
|
||||
}
|
||||
}
|
||||
|
@ -832,9 +838,7 @@ class ElectrumWorker {
|
|||
}
|
||||
|
||||
if (txVerbose?.isEmpty ?? true) {
|
||||
txHex = await _electrumClient!.request(
|
||||
ElectrumRequestGetTransactionHex(transactionHash: hash),
|
||||
);
|
||||
txHex = await _getTransactionHex(hash: hash);
|
||||
} else {
|
||||
txHex = txVerbose!['hex'] as String;
|
||||
}
|
||||
|
@ -869,10 +873,7 @@ class ElectrumWorker {
|
|||
final inputTransactionHexes = <String, String>{};
|
||||
|
||||
await Future.wait(original.inputs.map((e) => e.txId).toList().map((inHash) async {
|
||||
final hex = await _electrumClient!.request(
|
||||
ElectrumRequestGetTransactionHex(transactionHash: inHash),
|
||||
);
|
||||
|
||||
final hex = await _getTransactionHex(hash: inHash);
|
||||
inputTransactionHexes[inHash] = hex;
|
||||
}));
|
||||
|
||||
|
@ -1058,6 +1059,11 @@ class ElectrumWorker {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _handleGetTxHex(ElectrumWorkerTxHexRequest request) async {
|
||||
final hex = await _getTransactionHex(hash: request.txHash);
|
||||
_sendResponse(ElectrumWorkerTxHexResponse(hex: hex, id: request.id));
|
||||
}
|
||||
|
||||
Future<void> _handleGetTxExpanded(ElectrumWorkerTxExpandedRequest request) async {
|
||||
final tx = await _getTransactionExpanded(
|
||||
hash: request.txHash,
|
||||
|
@ -1129,11 +1135,8 @@ class ElectrumWorker {
|
|||
}
|
||||
} else {
|
||||
await Future.wait(hashes.map((hash) async {
|
||||
final history = await _electrumClient!.request(
|
||||
ElectrumRequestGetTransactionHex(transactionHash: hash),
|
||||
);
|
||||
|
||||
transactionHexes.add(history);
|
||||
final hex = await _getTransactionHex(hash: hash);
|
||||
transactionHexes.add(hex);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -1248,9 +1251,7 @@ class ElectrumWorker {
|
|||
time = transactionVerbose['time'] as int?;
|
||||
confirmations = transactionVerbose['confirmations'] as int?;
|
||||
} else {
|
||||
transactionHex = await _electrumClient!.request(
|
||||
ElectrumRequestGetTransactionHex(transactionHash: hash),
|
||||
);
|
||||
transactionHex = await _getTransactionHex(hash: hash);
|
||||
}
|
||||
|
||||
if (getTime && _walletType == WalletType.bitcoin) {
|
||||
|
@ -1271,11 +1272,7 @@ class ElectrumWorker {
|
|||
final ins = <BtcTransaction>[];
|
||||
|
||||
for (final vin in original.inputs) {
|
||||
final inputTransactionHex = await _electrumClient!.request(
|
||||
// TODO: _getTXHex
|
||||
ElectrumRequestGetTransactionHex(transactionHash: vin.txId),
|
||||
);
|
||||
|
||||
final inputTransactionHex = await _getTransactionHex(hash: vin.txId);
|
||||
ins.add(BtcTransaction.fromRaw(inputTransactionHex));
|
||||
}
|
||||
|
||||
|
@ -1311,12 +1308,7 @@ class ElectrumWorker {
|
|||
}
|
||||
} else {
|
||||
await Future.wait(hashes.map((hash) async {
|
||||
final hex = await _electrumClient!.request(
|
||||
ElectrumRequestGetTransactionHex(
|
||||
transactionHash: hash,
|
||||
),
|
||||
);
|
||||
|
||||
final hex = await _getTransactionHex(hash: hash);
|
||||
inputTransactionHexById[hash] = hex;
|
||||
}));
|
||||
}
|
||||
|
@ -1324,6 +1316,14 @@ class ElectrumWorker {
|
|||
return inputTransactionHexById;
|
||||
}
|
||||
|
||||
Future<String> _getTransactionHex({required String hash}) async {
|
||||
final hex = await _electrumClient!.request(
|
||||
ElectrumRequestGetTransactionHex(transactionHash: hash),
|
||||
);
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
Future<void> _handleGetFeeRates(ElectrumWorkerGetFeesRequest request) async {
|
||||
if (request.mempoolAPIEnabled && _walletType == WalletType.bitcoin) {
|
||||
try {
|
||||
|
@ -1350,7 +1350,7 @@ class ElectrumWorker {
|
|||
// this guarantees that, even if all fees are low and equal,
|
||||
// higher priority fee txs can be consumed when chain fees start surging
|
||||
|
||||
_sendResponse(
|
||||
return _sendResponse(
|
||||
ElectrumWorkerGetFeesResponse(
|
||||
result: BitcoinAPITransactionPriorities(
|
||||
minimum: minimum,
|
||||
|
@ -1362,24 +1362,17 @@ class ElectrumWorker {
|
|||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
_sendResponse(
|
||||
ElectrumWorkerGetFeesResponse(
|
||||
result: ElectrumTransactionPriorities.fromList(
|
||||
await _electrumClient!.getFeeRates(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_sendResponse(
|
||||
ElectrumWorkerGetFeesResponse(
|
||||
result: ElectrumTransactionPriorities.fromList(
|
||||
await _electrumClient!.getFeeRates(),
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// If the above didn't run or failed, fallback to Electrum fees anyway
|
||||
_sendResponse(
|
||||
ElectrumWorkerGetFeesResponse(
|
||||
result: ElectrumTransactionPriorities.fromList(
|
||||
await _electrumClient!.getFeeRates(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleCheckTweaks(ElectrumWorkerCheckTweaksRequest request) async {
|
||||
|
@ -1440,11 +1433,11 @@ class ElectrumWorker {
|
|||
}
|
||||
|
||||
// Initial status UI update, send how many blocks in total to scan
|
||||
// TODO: isSingleScan : dont update restoreHeight
|
||||
_sendResponse(ElectrumWorkerTweaksSubscribeResponse(
|
||||
result: TweaksSyncResponse(
|
||||
height: syncHeight,
|
||||
syncStatus: StartingScanSyncStatus(syncHeight),
|
||||
wasSingleBlock: scanData.isSingleScan,
|
||||
),
|
||||
));
|
||||
|
||||
|
@ -1493,7 +1486,11 @@ class ElectrumWorker {
|
|||
? SyncingSyncStatus(1, 0)
|
||||
: SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight);
|
||||
_sendResponse(ElectrumWorkerTweaksSubscribeResponse(
|
||||
result: TweaksSyncResponse(height: syncHeight, syncStatus: syncingStatus),
|
||||
result: TweaksSyncResponse(
|
||||
height: syncHeight,
|
||||
syncStatus: syncingStatus,
|
||||
wasSingleBlock: scanData.isSingleScan,
|
||||
),
|
||||
));
|
||||
|
||||
final tweakHeight = response.block;
|
||||
|
@ -1509,20 +1506,21 @@ class ElectrumWorker {
|
|||
try {
|
||||
final addToWallet = {};
|
||||
|
||||
receivers.forEach((receiver) {
|
||||
// scanOutputs called from rust here
|
||||
final scanResult = scanOutputs(outputPubkeys.keys.toList(), tweak, receiver);
|
||||
// receivers.forEach((receiver) {
|
||||
// scanOutputs called from rust here
|
||||
final receiver = receivers.first;
|
||||
final scanResult = scanOutputs([outputPubkeys.keys.toList()], tweak, receiver);
|
||||
|
||||
if (scanResult.isEmpty) {
|
||||
return;
|
||||
}
|
||||
if (scanResult.isEmpty) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (addToWallet[receiver.BSpend] == null) {
|
||||
addToWallet[receiver.BSpend] = scanResult;
|
||||
} else {
|
||||
addToWallet[receiver.BSpend].addAll(scanResult);
|
||||
}
|
||||
});
|
||||
if (addToWallet[receiver.BSpend] == null) {
|
||||
addToWallet[receiver.BSpend] = scanResult;
|
||||
} else {
|
||||
addToWallet[receiver.BSpend].addAll(scanResult);
|
||||
}
|
||||
// });
|
||||
|
||||
if (addToWallet.isEmpty) {
|
||||
// no results tx, continue to next tx
|
||||
|
@ -1588,6 +1586,7 @@ class ElectrumWorker {
|
|||
_sendResponse(ElectrumWorkerTweaksSubscribeResponse(
|
||||
result: TweaksSyncResponse(
|
||||
transactions: {txInfo.id: TweakResponseData(txInfo: txInfo, unspents: unspents)},
|
||||
wasSingleBlock: scanData.isSingleScan,
|
||||
),
|
||||
));
|
||||
|
||||
|
@ -1604,7 +1603,7 @@ class ElectrumWorker {
|
|||
|
||||
syncHeight = tweakHeight;
|
||||
|
||||
if (tweakHeight >= scanData.chainTip || scanData.isSingleScan) {
|
||||
if ((tweakHeight >= scanData.chainTip) || scanData.isSingleScan) {
|
||||
_sendResponse(
|
||||
ElectrumWorkerTweaksSubscribeResponse(
|
||||
result: TweaksSyncResponse(
|
||||
|
@ -1612,6 +1611,7 @@ class ElectrumWorker {
|
|||
syncStatus: scanData.isSingleScan
|
||||
? SyncedSyncStatus()
|
||||
: SyncedTipSyncStatus(scanData.chainTip),
|
||||
wasSingleBlock: scanData.isSingleScan,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -1627,15 +1627,7 @@ class ElectrumWorker {
|
|||
|
||||
Future<void> _handleGetVersion(ElectrumWorkerGetVersionRequest request) async {
|
||||
_sendResponse(
|
||||
ElectrumWorkerGetVersionResponse(
|
||||
result: await _electrumClient!.request(
|
||||
ElectrumRequestVersion(
|
||||
clientName: "",
|
||||
protocolVersion: "1.4",
|
||||
),
|
||||
),
|
||||
id: request.id,
|
||||
),
|
||||
ElectrumWorkerGetVersionResponse(result: version!, id: request.id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,14 @@ class ElectrumWorkerMethods {
|
|||
static const String connectionMethod = "connection";
|
||||
static const String unknownMethod = "unknown";
|
||||
static const String txHashMethod = "txHash";
|
||||
static const String txHexMethod = "txHex";
|
||||
static const String checkTweaksMethod = "checkTweaks";
|
||||
static const String stopScanningMethod = "stopScanning";
|
||||
|
||||
static const ElectrumWorkerMethods connect = ElectrumWorkerMethods._(connectionMethod);
|
||||
static const ElectrumWorkerMethods unknown = ElectrumWorkerMethods._(unknownMethod);
|
||||
static const ElectrumWorkerMethods txHash = ElectrumWorkerMethods._(txHashMethod);
|
||||
static const ElectrumWorkerMethods txHex = ElectrumWorkerMethods._(txHexMethod);
|
||||
static const ElectrumWorkerMethods checkTweaks = ElectrumWorkerMethods._(checkTweaksMethod);
|
||||
static const ElectrumWorkerMethods stopScanning = ElectrumWorkerMethods._(stopScanningMethod);
|
||||
|
||||
|
|
77
cw_bitcoin/lib/electrum_worker/methods/get_tx_hex.dart
Normal file
77
cw_bitcoin/lib/electrum_worker/methods/get_tx_hex.dart
Normal file
|
@ -0,0 +1,77 @@
|
|||
part of 'methods.dart';
|
||||
|
||||
class ElectrumWorkerTxHexRequest implements ElectrumWorkerRequest {
|
||||
ElectrumWorkerTxHexRequest({
|
||||
required this.txHash,
|
||||
required this.currentChainTip,
|
||||
this.mempoolAPIEnabled = false,
|
||||
this.id,
|
||||
this.completed = false,
|
||||
});
|
||||
|
||||
final String txHash;
|
||||
final int currentChainTip;
|
||||
final bool mempoolAPIEnabled;
|
||||
final int? id;
|
||||
final bool completed;
|
||||
|
||||
@override
|
||||
final String method = ElectrumWorkerMethods.txHex.method;
|
||||
|
||||
@override
|
||||
factory ElectrumWorkerTxHexRequest.fromJson(Map<String, dynamic> json) {
|
||||
return ElectrumWorkerTxHexRequest(
|
||||
txHash: json['txHash'] as String,
|
||||
currentChainTip: json['currentChainTip'] as int,
|
||||
mempoolAPIEnabled: json['mempoolAPIEnabled'] as bool,
|
||||
id: json['id'] as int?,
|
||||
completed: json['completed'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'method': method,
|
||||
'id': id,
|
||||
'completed': completed,
|
||||
'txHash': txHash,
|
||||
'currentChainTip': currentChainTip,
|
||||
'mempoolAPIEnabled': mempoolAPIEnabled,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ElectrumWorkerTxHexError extends ElectrumWorkerErrorResponse {
|
||||
ElectrumWorkerTxHexError({
|
||||
required String error,
|
||||
super.id,
|
||||
}) : super(error: error);
|
||||
|
||||
@override
|
||||
String get method => ElectrumWorkerMethods.txHex.method;
|
||||
}
|
||||
|
||||
class ElectrumWorkerTxHexResponse extends ElectrumWorkerResponse<String, String> {
|
||||
ElectrumWorkerTxHexResponse({
|
||||
required String hex,
|
||||
super.error,
|
||||
super.id,
|
||||
super.completed,
|
||||
}) : super(result: hex, method: ElectrumWorkerMethods.txHex.method);
|
||||
|
||||
@override
|
||||
String resultJson(result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
factory ElectrumWorkerTxHexResponse.fromJson(Map<String, dynamic> json) {
|
||||
return ElectrumWorkerTxHexResponse(
|
||||
hex: json['result'] as String,
|
||||
error: json['error'] as String?,
|
||||
id: json['id'] as int?,
|
||||
completed: json['completed'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ part 'scripthashes_subscribe.dart';
|
|||
part 'get_balance.dart';
|
||||
part 'get_history.dart';
|
||||
part 'get_tx_expanded.dart';
|
||||
part 'get_tx_hex.dart';
|
||||
part 'broadcast.dart';
|
||||
part 'list_unspent.dart';
|
||||
part 'tweaks_subscribe.dart';
|
||||
|
|
|
@ -143,14 +143,21 @@ class TweaksSyncResponse {
|
|||
int? height;
|
||||
SyncStatus? syncStatus;
|
||||
Map<String, TweakResponseData>? transactions = {};
|
||||
final bool wasSingleBlock;
|
||||
|
||||
TweaksSyncResponse({this.height, this.syncStatus, this.transactions});
|
||||
TweaksSyncResponse({
|
||||
required this.wasSingleBlock,
|
||||
this.height,
|
||||
this.syncStatus,
|
||||
this.transactions,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'height': height,
|
||||
'syncStatus': syncStatus == null ? null : syncStatusToJson(syncStatus!),
|
||||
'transactions': transactions?.map((key, value) => MapEntry(key, value.toJson())),
|
||||
'wasSingleBlock': wasSingleBlock,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -168,6 +175,7 @@ class TweaksSyncResponse {
|
|||
TweakResponseData.fromJson(value as Map<String, dynamic>),
|
||||
),
|
||||
),
|
||||
wasSingleBlock: json['wasSingleBlock'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class ElectrumWorkerGetVersionError extends ElectrumWorkerErrorResponse {
|
|||
String get method => ElectrumRequestMethods.version.method;
|
||||
}
|
||||
|
||||
class ElectrumWorkerGetVersionResponse extends ElectrumWorkerResponse<List<String>, List<String>> {
|
||||
class ElectrumWorkerGetVersionResponse extends ElectrumWorkerResponse<String, String> {
|
||||
ElectrumWorkerGetVersionResponse({
|
||||
required super.result,
|
||||
super.error,
|
||||
|
@ -49,14 +49,14 @@ class ElectrumWorkerGetVersionResponse extends ElectrumWorkerResponse<List<Strin
|
|||
}) : super(method: ElectrumRequestMethods.version.method);
|
||||
|
||||
@override
|
||||
List<String> resultJson(result) {
|
||||
return result;
|
||||
String resultJson(result) {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
factory ElectrumWorkerGetVersionResponse.fromJson(Map<String, dynamic> json) {
|
||||
return ElectrumWorkerGetVersionResponse(
|
||||
result: json['result'] as List<String>,
|
||||
result: json['result'] as String,
|
||||
error: json['error'] as String?,
|
||||
id: json['id'] as int?,
|
||||
completed: json['completed'] as bool? ?? false,
|
||||
|
|
|
@ -7,15 +7,24 @@ class ServerCapability {
|
|||
|
||||
bool supportsBatching;
|
||||
bool supportsTxVerbose;
|
||||
String version;
|
||||
|
||||
ServerCapability({required this.supportsBatching, required this.supportsTxVerbose});
|
||||
ServerCapability({
|
||||
required this.supportsBatching,
|
||||
required this.supportsTxVerbose,
|
||||
required this.version,
|
||||
});
|
||||
|
||||
static ServerCapability fromVersion(List<String> serverVersion) {
|
||||
if (serverVersion.isNotEmpty) {
|
||||
final server = serverVersion.first.toLowerCase();
|
||||
|
||||
if (server.contains('electrumx')) {
|
||||
return ServerCapability(supportsBatching: true, supportsTxVerbose: true);
|
||||
return ServerCapability(
|
||||
supportsBatching: true,
|
||||
supportsTxVerbose: true,
|
||||
version: server,
|
||||
);
|
||||
}
|
||||
|
||||
if (server.startsWith('electrs/')) {
|
||||
|
@ -28,13 +37,21 @@ class ServerCapability {
|
|||
try {
|
||||
final version = ElectrumVersion.fromStr(electrsVersion);
|
||||
if (version.compareTo(ELECTRS_MIN_BATCHING_VERSION) >= 0) {
|
||||
return ServerCapability(supportsBatching: true, supportsTxVerbose: false);
|
||||
return ServerCapability(
|
||||
supportsBatching: true,
|
||||
supportsTxVerbose: false,
|
||||
version: server,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore version parsing errors
|
||||
}
|
||||
|
||||
return ServerCapability(supportsBatching: false, supportsTxVerbose: false);
|
||||
return ServerCapability(
|
||||
supportsBatching: false,
|
||||
supportsTxVerbose: false,
|
||||
version: server,
|
||||
);
|
||||
}
|
||||
|
||||
if (server.startsWith('fulcrum')) {
|
||||
|
@ -43,7 +60,11 @@ class ServerCapability {
|
|||
try {
|
||||
final version = ElectrumVersion.fromStr(fulcrumVersion);
|
||||
if (version.compareTo(FULCRUM_MIN_BATCHING_VERSION) >= 0) {
|
||||
return ServerCapability(supportsBatching: true, supportsTxVerbose: true);
|
||||
return ServerCapability(
|
||||
supportsBatching: true,
|
||||
supportsTxVerbose: true,
|
||||
version: server,
|
||||
);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
@ -59,7 +80,11 @@ class ServerCapability {
|
|||
try {
|
||||
final version = ElectrumVersion.fromStr(mempoolElectrsVersion);
|
||||
if (version.compareTo(MEMPOOL_ELECTRS_MIN_BATCHING_VERSION) > 0) {
|
||||
return ServerCapability(supportsBatching: true, supportsTxVerbose: false);
|
||||
return ServerCapability(
|
||||
supportsBatching: true,
|
||||
supportsTxVerbose: false,
|
||||
version: server,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore version parsing errors
|
||||
|
@ -67,6 +92,10 @@ class ServerCapability {
|
|||
}
|
||||
}
|
||||
|
||||
return ServerCapability(supportsBatching: false, supportsTxVerbose: false);
|
||||
return ServerCapability(
|
||||
supportsBatching: false,
|
||||
supportsTxVerbose: false,
|
||||
version: "unknown",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,13 +72,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
mwebEnabled = alwaysScan ?? false;
|
||||
|
||||
if (walletAddressesSnapshot != null) {
|
||||
// walletAddresses = LitecoinWalletAddresses.fromJson(
|
||||
// walletAddressesSnapshot,
|
||||
// walletInfo,
|
||||
// network: network,
|
||||
// isHardwareWallet: isHardwareWallet,
|
||||
// hdWallets: hdWallets,
|
||||
// );
|
||||
walletAddresses = LitecoinWalletAddressesBase.fromJson(
|
||||
walletAddressesSnapshot,
|
||||
walletInfo,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
hdWallets: hdWallets,
|
||||
);
|
||||
} else {
|
||||
walletAddresses = LitecoinWalletAddresses(
|
||||
walletInfo,
|
||||
|
@ -186,7 +186,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
snp = await LitecoinWalletSnapshot.load(
|
||||
encryptionFileUtils,
|
||||
name,
|
||||
walletInfo.type,
|
||||
walletInfo,
|
||||
password,
|
||||
LitecoinNetwork.mainnet,
|
||||
);
|
||||
|
@ -347,6 +347,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
// if the confirmations haven't changed, skip updating:
|
||||
if (tx.confirmations == confirmations) continue;
|
||||
|
||||
// if an outgoing tx is now confirmed, delete the utxo from the box (delete the unspent coin):
|
||||
if (confirmations >= 2 && tx.direction == TransactionDirection.outgoing) {
|
||||
for (var coin in unspentCoins) {
|
||||
if (tx.inputAddresses?.contains(coin.address) ?? false) {
|
||||
final utxo = mwebUtxosBox.get(coin.address);
|
||||
if (utxo != null) {
|
||||
printV("deleting utxo ${coin.address} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
await mwebUtxosBox.delete(coin.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.confirmations = confirmations;
|
||||
tx.isPending = false;
|
||||
transactionHistory.addOne(tx);
|
||||
|
@ -786,84 +799,85 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
// @override
|
||||
// Future<ElectrumBalance> fetchBalances() async {
|
||||
// final balance = await super.fetchBalances();
|
||||
Future<void> updateBalance([Set<String>? scripthashes, bool? wait]) async {
|
||||
await super.updateBalance(scripthashes, true);
|
||||
final balance = this.balance[currency]!;
|
||||
|
||||
// if (!mwebEnabled) {
|
||||
// return balance;
|
||||
// }
|
||||
if (!mwebEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// // update unspent balances:
|
||||
// await updateUnspent();
|
||||
// update unspent balances:
|
||||
await updateUnspent();
|
||||
|
||||
// int confirmed = balance.confirmed;
|
||||
// int unconfirmed = balance.unconfirmed;
|
||||
// int confirmedMweb = 0;
|
||||
// int unconfirmedMweb = 0;
|
||||
// try {
|
||||
// mwebUtxosBox.values.forEach((utxo) {
|
||||
// if (utxo.height > 0) {
|
||||
// confirmedMweb += utxo.value.toInt();
|
||||
// } else {
|
||||
// unconfirmedMweb += utxo.value.toInt();
|
||||
// }
|
||||
// });
|
||||
// if (unconfirmedMweb > 0) {
|
||||
// unconfirmedMweb = -1 * (confirmedMweb - unconfirmedMweb);
|
||||
// }
|
||||
// } catch (_) {}
|
||||
int confirmed = balance.confirmed;
|
||||
int unconfirmed = balance.unconfirmed;
|
||||
int confirmedMweb = 0;
|
||||
int unconfirmedMweb = 0;
|
||||
try {
|
||||
mwebUtxosBox.values.forEach((utxo) {
|
||||
if (utxo.height > 0) {
|
||||
confirmedMweb += utxo.value.toInt();
|
||||
} else {
|
||||
unconfirmedMweb += utxo.value.toInt();
|
||||
}
|
||||
});
|
||||
if (unconfirmedMweb > 0) {
|
||||
unconfirmedMweb = -1 * (confirmedMweb - unconfirmedMweb);
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// for (var addressRecord in walletAddresses.allAddresses) {
|
||||
// addressRecord.balance = 0;
|
||||
// addressRecord.txCount = 0;
|
||||
// }
|
||||
for (var addressRecord in walletAddresses.allAddresses) {
|
||||
addressRecord.balance = 0;
|
||||
addressRecord.txCount = 0;
|
||||
}
|
||||
|
||||
// unspentCoins.forEach((coin) {
|
||||
// final coinInfoList = unspentCoinsInfo.values.where(
|
||||
// (element) =>
|
||||
// element.walletId.contains(id) &&
|
||||
// element.hash.contains(coin.hash) &&
|
||||
// element.vout == coin.vout,
|
||||
// );
|
||||
unspentCoins.forEach((coin) {
|
||||
final coinInfoList = unspentCoinsInfo.values.where(
|
||||
(element) =>
|
||||
element.walletId.contains(id) &&
|
||||
element.hash.contains(coin.hash) &&
|
||||
element.vout == coin.vout,
|
||||
);
|
||||
|
||||
// if (coinInfoList.isNotEmpty) {
|
||||
// final coinInfo = coinInfoList.first;
|
||||
if (coinInfoList.isNotEmpty) {
|
||||
final coinInfo = coinInfoList.first;
|
||||
|
||||
// coin.isFrozen = coinInfo.isFrozen;
|
||||
// coin.isSending = coinInfo.isSending;
|
||||
// coin.note = coinInfo.note;
|
||||
// coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
// } else {
|
||||
// super.addCoinInfo(coin);
|
||||
// }
|
||||
// });
|
||||
coin.isFrozen = coinInfo.isFrozen;
|
||||
coin.isSending = coinInfo.isSending;
|
||||
coin.note = coinInfo.note;
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
} else {
|
||||
super.addCoinInfo(coin);
|
||||
}
|
||||
});
|
||||
|
||||
// // update the txCount for each address using the tx history, since we can't rely on mwebd
|
||||
// // to have an accurate count, we should just keep it in sync with what we know from the tx history:
|
||||
// for (final tx in transactionHistory.transactions.values) {
|
||||
// // if (tx.isPending) continue;
|
||||
// if (tx.inputAddresses == null || tx.outputAddresses == null) {
|
||||
// continue;
|
||||
// }
|
||||
// final txAddresses = tx.inputAddresses! + tx.outputAddresses!;
|
||||
// for (final address in txAddresses) {
|
||||
// final addressRecord = walletAddresses.allAddresses
|
||||
// .firstWhereOrNull((addressRecord) => addressRecord.address == address);
|
||||
// if (addressRecord == null) {
|
||||
// continue;
|
||||
// }
|
||||
// addressRecord.txCount++;
|
||||
// }
|
||||
// }
|
||||
// update the txCount for each address using the tx history, since we can't rely on mwebd
|
||||
// to have an accurate count, we should just keep it in sync with what we know from the tx history:
|
||||
for (final tx in transactionHistory.transactions.values) {
|
||||
// if (tx.isPending) continue;
|
||||
if (tx.inputAddresses == null || tx.outputAddresses == null) {
|
||||
continue;
|
||||
}
|
||||
final txAddresses = tx.inputAddresses! + tx.outputAddresses!;
|
||||
for (final address in txAddresses) {
|
||||
final addressRecord = walletAddresses.allAddresses
|
||||
.firstWhereOrNull((addressRecord) => addressRecord.address == address);
|
||||
if (addressRecord == null) {
|
||||
continue;
|
||||
}
|
||||
addressRecord.txCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// return ElectrumBalance(
|
||||
// confirmed: confirmed,
|
||||
// unconfirmed: unconfirmed,
|
||||
// frozen: balance.frozen,
|
||||
// secondConfirmed: confirmedMweb,
|
||||
// secondUnconfirmed: unconfirmedMweb,
|
||||
// );
|
||||
// }
|
||||
this.balance[currency] = ElectrumBalance(
|
||||
confirmed: confirmed,
|
||||
unconfirmed: unconfirmed,
|
||||
frozen: balance.frozen,
|
||||
secondConfirmed: confirmedMweb,
|
||||
secondUnconfirmed: unconfirmedMweb,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
ElectrumTxCreateUtxoDetails createUTXOS({
|
||||
|
|
|
@ -72,8 +72,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
|
||||
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount;
|
||||
|
||||
final startIndex = (isChange ? changeAddresses : receiveAddresses)
|
||||
.where((addr) => addr.cwDerivationType == derivationType && addr.type == addressType)
|
||||
final startIndex = getAddressesByType(addressType, isChange)
|
||||
.where((addr) => (addr as BitcoinAddressRecord).cwDerivationType == derivationType)
|
||||
.length;
|
||||
|
||||
final mwebAddresses = <LitecoinMWEBAddressRecord>[];
|
||||
|
@ -268,7 +268,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
@override
|
||||
String get addressForExchange {
|
||||
// don't use mweb addresses for exchange refund address:
|
||||
final addresses = receiveAddresses
|
||||
final addresses = selectedReceiveAddresses
|
||||
.where((element) => element.type == SegwitAddressType.p2wpkh && !element.isUsed);
|
||||
return addresses.first.address;
|
||||
}
|
||||
|
@ -329,6 +329,64 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
addressesSet.addAll(addresses);
|
||||
this.mwebAddresses.clear();
|
||||
this.mwebAddresses.addAll(addressesSet);
|
||||
updateAddressesOnReceiveScreen();
|
||||
updateAddressesByType();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = super.toJson();
|
||||
json['mwebAddresses'] = mwebAddresses.map((address) => address.toJSON()).toList();
|
||||
// json['mwebAddressIndex'] =
|
||||
return json;
|
||||
}
|
||||
|
||||
static Map<String, dynamic> fromSnapshot(Map<dynamic, dynamic> data) {
|
||||
final electrumSnapshot = ElectrumWalletAddressesBase.fromSnapshot(data);
|
||||
|
||||
final mwebAddresses = data['mweb_addresses'] as List? ??
|
||||
<Object>[].map((e) => LitecoinMWEBAddressRecord.fromJSON(e as String)).toList();
|
||||
|
||||
// var mwebAddressIndex = 0;
|
||||
|
||||
// try {
|
||||
// mwebAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');
|
||||
// } catch (_) {}
|
||||
|
||||
return {
|
||||
'allAddresses': electrumSnapshot["addresses"],
|
||||
'addressPageType': data['address_page_type'] as String?,
|
||||
'receiveAddressIndexByType': electrumSnapshot["receiveAddressIndexByType"],
|
||||
'changeAddressIndexByType': electrumSnapshot["changeAddressIndexByType"],
|
||||
'mwebAddresses': mwebAddresses,
|
||||
};
|
||||
}
|
||||
|
||||
static LitecoinWalletAddressesBase fromJson(
|
||||
Map<String, dynamic> json,
|
||||
WalletInfo walletInfo, {
|
||||
required Map<CWBitcoinDerivationType, Bip32Slip10Secp256k1> hdWallets,
|
||||
required BasedUtxoNetwork network,
|
||||
required bool isHardwareWallet,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
List<LitecoinMWEBAddressRecord>? initialMwebAddresses,
|
||||
}) {
|
||||
initialAddresses ??= (json['allAddresses'] as List)
|
||||
.map((record) => BitcoinAddressRecord.fromJSON(record as String))
|
||||
.toList();
|
||||
|
||||
initialMwebAddresses ??= (json['mwebAddresses'] as List)
|
||||
.map(
|
||||
(address) => LitecoinMWEBAddressRecord.fromJSON(address as String),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return LitecoinWalletAddresses(
|
||||
walletInfo,
|
||||
hdWallets: hdWallets,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialMwebAddresses: initialMwebAddresses,
|
||||
mwebEnabled: true, // TODO
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
|||
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
||||
class LitecoinWalletSnapshot extends ElectrumWalletSnapshot {
|
||||
LitecoinWalletSnapshot({
|
||||
|
@ -29,10 +29,11 @@ class LitecoinWalletSnapshot extends ElectrumWalletSnapshot {
|
|||
static Future<LitecoinWalletSnapshot> load(
|
||||
EncryptionFileUtils encryptionFileUtils,
|
||||
String name,
|
||||
WalletType type,
|
||||
WalletInfo walletInfo,
|
||||
String password,
|
||||
BasedUtxoNetwork network,
|
||||
) async {
|
||||
final type = walletInfo.type;
|
||||
final path = await pathForWallet(name: name, type: type);
|
||||
final jsonSource = await encryptionFileUtils.read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
|
@ -40,7 +41,7 @@ class LitecoinWalletSnapshot extends ElectrumWalletSnapshot {
|
|||
final ElectrumWalletSnapshot electrumWalletSnapshot = await ElectrumWalletSnapshot.load(
|
||||
encryptionFileUtils,
|
||||
name,
|
||||
type,
|
||||
walletInfo,
|
||||
password,
|
||||
network,
|
||||
);
|
||||
|
|
|
@ -121,7 +121,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
snp = await ElectrumWalletSnapshot.load(
|
||||
encryptionFileUtils,
|
||||
name,
|
||||
walletInfo.type,
|
||||
walletInfo,
|
||||
password,
|
||||
BitcoinCashNetwork.mainnet,
|
||||
);
|
||||
|
|
|
@ -23,6 +23,9 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
|
|||
@override
|
||||
Future<void> init() async {
|
||||
await generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
allAddresses.forEach((addr) {
|
||||
print(addr.address);
|
||||
});
|
||||
await super.init();
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,10 @@ class CWBitcoin extends Bitcoin {
|
|||
@computed
|
||||
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
|
||||
final electrumWallet = wallet as ElectrumWallet;
|
||||
return electrumWallet.walletAddresses.addressesOnReceiveScreen
|
||||
return [
|
||||
...electrumWallet.walletAddresses.selectedReceiveAddresses,
|
||||
...electrumWallet.walletAddresses.selectedChangeAddresses
|
||||
]
|
||||
.map(
|
||||
(addr) => ElectrumSubAddress(
|
||||
id: addr.index,
|
||||
|
|
|
@ -9,7 +9,7 @@ import 'package:cw_core/transaction_priority.dart';
|
|||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
List<TransactionPriority> priorityForWalletType(WalletBase wallet) {
|
||||
List<TransactionPriority> priorityForWallet(WalletBase wallet) {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return monero!.getTransactionPriorities();
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/address_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
|
||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/share_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
|
@ -102,13 +116,13 @@ class ReceivePage extends BasePage {
|
|||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 50, 24, 24),
|
||||
child: QRWidget(
|
||||
addressListViewModel: addressListViewModel,
|
||||
formKey: _formKey,
|
||||
heroTag: _heroTag,
|
||||
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||
amountController: _amountController,
|
||||
isLight: currentTheme.type == ThemeType.light,
|
||||
),
|
||||
addressListViewModel: addressListViewModel,
|
||||
formKey: _formKey,
|
||||
heroTag: _heroTag,
|
||||
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||
amountController: _amountController,
|
||||
isLight: currentTheme.type == ThemeType.light,
|
||||
),
|
||||
),
|
||||
AddressList(addressListViewModel: addressListViewModel),
|
||||
Padding(
|
||||
|
|
|
@ -529,7 +529,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
|||
}
|
||||
|
||||
Future<void> pickTransactionPriority(BuildContext context) async {
|
||||
final items = priorityForWalletType(sendViewModel.wallet);
|
||||
final items = priorityForWallet(sendViewModel.wallet);
|
||||
final selectedItem = items.indexOf(sendViewModel.transactionPriority);
|
||||
final customItemIndex = sendViewModel.getCustomPriorityIndex(items);
|
||||
final isBitcoinWallet = sendViewModel.walletType == WalletType.bitcoin;
|
||||
|
|
|
@ -37,7 +37,7 @@ class OtherSettingsPage extends BasePage {
|
|||
_otherSettingsViewModel.walletType == WalletType.bitcoin
|
||||
? SettingsPriorityPickerCell(
|
||||
title: S.current.settings_fee_priority,
|
||||
items: priorityForWalletType(_otherSettingsViewModel.sendViewModel.wallet),
|
||||
items: priorityForWallet(_otherSettingsViewModel.sendViewModel.wallet),
|
||||
displayItem: _otherSettingsViewModel.getDisplayBitcoinPriority,
|
||||
selectedItem: _otherSettingsViewModel.transactionPriority,
|
||||
customItemIndex: _otherSettingsViewModel.customPriorityItemIndex,
|
||||
|
@ -47,7 +47,7 @@ class OtherSettingsPage extends BasePage {
|
|||
)
|
||||
: SettingsPickerCell(
|
||||
title: S.current.settings_fee_priority,
|
||||
items: priorityForWalletType(_otherSettingsViewModel.sendViewModel.wallet),
|
||||
items: priorityForWallet(_otherSettingsViewModel.sendViewModel.wallet),
|
||||
displayItem: _otherSettingsViewModel.getDisplayPriority,
|
||||
selectedItem: _otherSettingsViewModel.transactionPriority,
|
||||
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
|
||||
|
|
|
@ -92,7 +92,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
setTransactionPriority(bitcoinTransactionPriorityMedium);
|
||||
}
|
||||
final priority = _settingsStore.priority[wallet.type];
|
||||
final priorities = priorityForWalletType(wallet);
|
||||
final priorities = priorityForWallet(wallet);
|
||||
if (priorities.isNotEmpty && !priorities.contains(priority)) {
|
||||
_settingsStore.priority[wallet.type] = priorities.first;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ abstract class OtherSettingsViewModelBase with Store {
|
|||
.then((PackageInfo packageInfo) => currentVersion = packageInfo.version);
|
||||
|
||||
final priority = _settingsStore.priority[_wallet.type];
|
||||
final priorities = priorityForWalletType(_wallet);
|
||||
final priorities = priorityForWallet(_wallet);
|
||||
|
||||
if (!priorities.contains(priority) && priorities.isNotEmpty) {
|
||||
_settingsStore.priority[_wallet.type] = priorities.first;
|
||||
|
@ -103,7 +103,7 @@ abstract class OtherSettingsViewModelBase with Store {
|
|||
double get customBitcoinFeeRate => _settingsStore.customBitcoinFeeRate.toDouble();
|
||||
|
||||
int? get customPriorityItemIndex {
|
||||
final priorities = priorityForWalletType(_wallet);
|
||||
final priorities = priorityForWallet(_wallet);
|
||||
final customItem = priorities
|
||||
.firstWhereOrNull((element) => element == bitcoin!.getBitcoinTransactionPriorityCustom());
|
||||
return customItem != null ? priorities.indexOf(customItem) : null;
|
||||
|
|
|
@ -566,7 +566,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
|||
StandartListItem(title: 'New recommended fee rate', value: '$recommendedRate sat/byte'));
|
||||
}
|
||||
|
||||
final priorities = priorityForWalletType(wallet);
|
||||
final priorities = priorityForWallet(wallet);
|
||||
final selectedItem = priorities.indexOf(sendViewModel.transactionPriority);
|
||||
final customItem = priorities.firstWhereOrNull(
|
||||
(element) => element.title == sendViewModel.bitcoinTransactionPriorityCustom.title);
|
||||
|
|
Loading…
Reference in a new issue