mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
fix: discovery & startSync [skip ci]
This commit is contained in:
parent
67ac4e8d97
commit
2502a14b50
7 changed files with 337 additions and 220 deletions
|
@ -114,36 +114,20 @@ abstract class BitcoinWalletBase extends ElectrumWallet<BitcoinWalletAddresses>
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initAddresses() async {
|
Future<bool?> initAddresses([bool? sync]) async {
|
||||||
// if (didInitAddresses) return;
|
var isDiscovered = await super.initAddresses(sync);
|
||||||
|
bool? discovered;
|
||||||
|
|
||||||
// If already loaded, no need to generate/discover all initial addresses
|
// NOTE: will initiate by priority from the first walletAddressTypes
|
||||||
// so skip
|
// then proceeds to following ones after got fully discovered response from worker response
|
||||||
// if (!walletAddresses.loadedFromNewSnapshot) {
|
if (isDiscovered != false) {
|
||||||
for (final walletAddressType in walletAddresses.walletAddressTypes) {
|
for (final addressType in walletAddresses.walletAddressTypes) {
|
||||||
if (isHardwareWallet && walletAddressType != SegwitAddressType.p2wpkh) continue;
|
if (isHardwareWallet && addressType != SegwitAddressType.p2wpkh) continue;
|
||||||
|
|
||||||
for (final seedBytesType in hdWallets.keys) {
|
for (final seedBytesType in hdWallets.keys) {
|
||||||
generateInitialAddresses(
|
|
||||||
addressType: walletAddressType,
|
|
||||||
seedBytesType: seedBytesType,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@action
|
|
||||||
void generateInitialAddresses({
|
|
||||||
required BitcoinAddressType addressType,
|
|
||||||
required SeedBytesType seedBytesType,
|
|
||||||
BitcoinDerivationInfo? bitcoinDerivationInfo,
|
|
||||||
}) {
|
|
||||||
// p2wpkh has always had the right derivations, skip if creating old derivations
|
// p2wpkh has always had the right derivations, skip if creating old derivations
|
||||||
if (seedBytesType.isOldDerivation && addressType == SegwitAddressType.p2wpkh) {
|
if (seedBytesType.isOldDerivation && addressType == SegwitAddressType.p2wpkh) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final bitcoinDerivationInfo = BitcoinAddressUtils.getDerivationFromType(
|
final bitcoinDerivationInfo = BitcoinAddressUtils.getDerivationFromType(
|
||||||
|
@ -152,30 +136,70 @@ abstract class BitcoinWalletBase extends ElectrumWallet<BitcoinWalletAddresses>
|
||||||
isElectrum: seedBytesType.isElectrum,
|
isElectrum: seedBytesType.isElectrum,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (seedBytesType.isOldDerivation) {
|
bool alreadyDidDerivation = false;
|
||||||
|
|
||||||
for (final derivationInfo in [
|
for (final derivationInfo in [
|
||||||
bitcoinDerivationInfo,
|
bitcoinDerivationInfo,
|
||||||
BitcoinDerivationInfos.ELECTRUM,
|
|
||||||
BitcoinDerivationInfos.BIP84,
|
BitcoinDerivationInfos.BIP84,
|
||||||
|
BitcoinDerivationInfos.ELECTRUM,
|
||||||
]) {
|
]) {
|
||||||
if (derivationInfo.derivationPath.toString() ==
|
final derivationPath = derivationInfo.derivationPath.toString();
|
||||||
bitcoinDerivationInfo.derivationPath.toString()) {
|
|
||||||
|
if (alreadyDidDerivation &&
|
||||||
|
derivationPath == bitcoinDerivationInfo.derivationPath.toString()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.generateInitialAddresses(
|
alreadyDidDerivation = true;
|
||||||
|
|
||||||
|
for (final isChange in [true, false]) {
|
||||||
|
isDiscovered = walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
|
derivationPath: derivationPath,
|
||||||
|
isChange: isChange,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isDiscovered == false) {
|
||||||
|
break;
|
||||||
|
} else if (sync == true)
|
||||||
|
subscribeForStatuses(
|
||||||
|
walletAddresses.addressesRecords
|
||||||
|
.getRecords(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
|
derivationPath: derivationPath,
|
||||||
|
isChange: isChange,
|
||||||
|
)
|
||||||
|
.whereType<BitcoinAddressRecord>()
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDiscovered == false) {
|
||||||
|
discovered = await generateInitialAddresses(
|
||||||
addressType: addressType,
|
addressType: addressType,
|
||||||
seedBytesType: seedBytesType,
|
seedBytesType: seedBytesType,
|
||||||
bitcoinDerivationInfo: derivationInfo,
|
bitcoinDerivationInfo: derivationInfo,
|
||||||
);
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
super.generateInitialAddresses(
|
|
||||||
addressType: addressType,
|
|
||||||
seedBytesType: seedBytesType,
|
|
||||||
bitcoinDerivationInfo: bitcoinDerivationInfo,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDiscovered == false) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDiscovered == false) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDiscovered == true && sync == false)
|
||||||
|
initAddresses(true);
|
||||||
|
else if (isDiscovered == true)
|
||||||
|
syncStatus = SyncedSyncStatus();
|
||||||
|
else if (isDiscovered == false && discovered == false) initAddresses(sync);
|
||||||
|
|
||||||
|
return isDiscovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<BitcoinWallet> create({
|
static Future<BitcoinWallet> create({
|
||||||
|
|
|
@ -44,10 +44,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||||
@override
|
@override
|
||||||
final walletAddressTypes = [
|
final walletAddressTypes = [
|
||||||
SegwitAddressType.p2wpkh,
|
SegwitAddressType.p2wpkh,
|
||||||
// SegwitAddressType.p2tr,
|
SegwitAddressType.p2tr,
|
||||||
// P2shAddressType.p2wpkhInP2sh,
|
P2shAddressType.p2wpkhInP2sh,
|
||||||
// P2pkhAddressType.p2pkh,
|
P2pkhAddressType.p2pkh,
|
||||||
// SegwitAddressType.p2wsh,
|
SegwitAddressType.p2wsh,
|
||||||
];
|
];
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
|
|
|
@ -181,7 +181,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
break;
|
break;
|
||||||
case ElectrumRequestMethods.scripthashesSubscribeMethod:
|
case ElectrumRequestMethods.scripthashesSubscribeMethod:
|
||||||
final response = ElectrumWorkerScripthashesSubscribeResponse.fromJson(messageJson);
|
final response = ElectrumWorkerScripthashesSubscribeResponse.fromJson(messageJson);
|
||||||
await onScripthashesStatusResponse(response.result.last);
|
await onScripthashesStatusResponse(response.result);
|
||||||
break;
|
break;
|
||||||
case ElectrumRequestMethods.getBalanceMethod:
|
case ElectrumRequestMethods.getBalanceMethod:
|
||||||
final response = ElectrumWorkerGetBalanceResponse.fromJson(messageJson);
|
final response = ElectrumWorkerGetBalanceResponse.fromJson(messageJson);
|
||||||
|
@ -309,8 +309,44 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
Timer? _updateFeeRateTimer;
|
Timer? _updateFeeRateTimer;
|
||||||
static const int _autoSaveInterval = 1;
|
static const int _autoSaveInterval = 1;
|
||||||
|
|
||||||
void initAddresses() {
|
Future<bool?> initAddresses([bool? sync]) async {
|
||||||
throw UnimplementedError();
|
bool? isDiscovered = null;
|
||||||
|
|
||||||
|
// NOTE: will initiate by priority from the first walletAddressTypes
|
||||||
|
// then proceeds to following ones after got fully discovered response from worker response
|
||||||
|
for (final addressType in walletAddresses.walletAddressTypes) {
|
||||||
|
if (isHardwareWallet && addressType != SegwitAddressType.p2wpkh) continue;
|
||||||
|
|
||||||
|
final bitcoinDerivationInfo = BitcoinAddressUtils.getDerivationFromType(
|
||||||
|
addressType,
|
||||||
|
network: network,
|
||||||
|
isElectrum: walletAddresses.walletSeedBytesType.isElectrum,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final isChange in [true, false]) {
|
||||||
|
isDiscovered = walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: walletAddresses.walletSeedBytesType,
|
||||||
|
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||||
|
isChange: isChange,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isDiscovered == false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDiscovered == false) {
|
||||||
|
await generateInitialAddresses(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: walletAddresses.walletSeedBytesType,
|
||||||
|
bitcoinDerivationInfo: bitcoinDerivationInfo,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDiscovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
|
@ -324,49 +360,16 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
@override
|
@override
|
||||||
Future<void> startSync() async {
|
Future<void> startSync() async {
|
||||||
try {
|
try {
|
||||||
// if (syncStatus is SynchronizingSyncStatus) {
|
syncStatus = SynchronizingSyncStatus();
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// syncStatus = SynchronizingSyncStatus();
|
await subscribeForHeaders(true);
|
||||||
|
|
||||||
// // INFO: FIRST (always): Call subscribe for headers, wait for completion to update currentChainTip (needed for other methods)
|
await initAddresses(false);
|
||||||
// await subscribeForHeaders(true);
|
|
||||||
|
|
||||||
// final responses = await subscribeForStatuses(addresses, true);
|
await updateFeeRates();
|
||||||
|
|
||||||
// final addressesWithHistory = walletAddresses.allAddresses
|
_updateFeeRateTimer ??=
|
||||||
// .where((e) => scripthashesWithStatus.contains(e.scriptHash))
|
Timer.periodic(const Duration(seconds: 5), (timer) async => await updateFeeRates());
|
||||||
// .toList();
|
|
||||||
|
|
||||||
// // INFO: SECOND: Start loading transaction histories for every address, this will help discover addresses until the unused gap limit has been reached, which will help finding the full balance and unspents next
|
|
||||||
// await updateTransactions(addressesWithHistory);
|
|
||||||
|
|
||||||
// // INFO: THIRD: Get the full wallet's balance with all addresses considered
|
|
||||||
// await updateBalance(scripthashesWithStatus, true);
|
|
||||||
|
|
||||||
// await updateAllUnspents(scripthashesWithStatus, true);
|
|
||||||
|
|
||||||
// syncStatus = SyncedSyncStatus();
|
|
||||||
|
|
||||||
// // INFO: FOURTH: Get the latest recommended fee rates and start update timer
|
|
||||||
// await updateFeeRates();
|
|
||||||
// _updateFeeRateTimer ??=
|
|
||||||
// Timer.periodic(const Duration(seconds: 5), (timer) async => await updateFeeRates());
|
|
||||||
|
|
||||||
// await save();
|
|
||||||
} catch (e, stacktrace) {
|
|
||||||
printV(stacktrace);
|
|
||||||
printV("startSync $e");
|
|
||||||
syncStatus = FailedSyncStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> syncAddresses(List<BitcoinAddressRecord> addresses) async {
|
|
||||||
try {
|
|
||||||
updateTransactions(addresses);
|
|
||||||
updateBalance(scripthashesWithStatus);
|
|
||||||
updateAllUnspents(scripthashesWithStatus);
|
|
||||||
|
|
||||||
await save();
|
await save();
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
|
@ -1239,12 +1242,26 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> onScripthashesStatusResponse(ElectrumWorkerScripthashesResponse result) async {
|
Future<void> onScripthashesStatusResponse(
|
||||||
if (result.status == null) {
|
List<ElectrumWorkerScripthashesResponse> result,
|
||||||
|
) async {
|
||||||
|
final noItemsWithStatus = result.length == 1 && result.first.status == null;
|
||||||
|
if (noItemsWithStatus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scripthashesWithStatus.add(result.scripthash);
|
final addresses = walletAddresses.allAddresses
|
||||||
|
.where(
|
||||||
|
(address) => result.any((e) => e.status != null && e.scripthash == address.scriptHash),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final scripthashesWithStatus =
|
||||||
|
result.where((e) => e.status != null).map((e) => e.scripthash).toList();
|
||||||
|
|
||||||
|
updateTransactions(addresses);
|
||||||
|
updateBalance(scripthashesWithStatus);
|
||||||
|
updateAllUnspents(scripthashesWithStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -1292,7 +1309,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
final addressesWithHistory = <BitcoinAddressRecord>[];
|
final addressesWithHistory = <BitcoinAddressRecord>[];
|
||||||
|
|
||||||
if (histories.isNotEmpty) {
|
if (histories.isNotEmpty) {
|
||||||
await Future.wait(histories.map((addressHistory) async {
|
for (final addressHistory in histories) {
|
||||||
final txs = addressHistory.txs;
|
final txs = addressHistory.txs;
|
||||||
|
|
||||||
if (txs.isNotEmpty) {
|
if (txs.isNotEmpty) {
|
||||||
|
@ -1302,20 +1319,11 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
transactionHistory.addOne(tx);
|
transactionHistory.addOne(tx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
|
||||||
if (addressesWithHistory.isNotEmpty) {
|
if (addressesWithHistory.isNotEmpty) {
|
||||||
walletAddresses.updateAddresses(addressesWithHistory);
|
walletAddresses.addAddresses(addressesWithHistory);
|
||||||
}
|
}
|
||||||
} else if (response.completed) {
|
|
||||||
final firstAddress = addressesWithHistory.first;
|
|
||||||
|
|
||||||
discoverNewAddresses(
|
|
||||||
seedBytesType: firstAddress.seedBytesType!,
|
|
||||||
isChange: firstAddress.isChange,
|
|
||||||
addressType: firstAddress.type,
|
|
||||||
derivationInfo: firstAddress.derivationInfo,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1675,7 +1683,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<List<ElectrumWorkerScripthashesResponse>> subscribeForStatuses([
|
Future<List<ElectrumWorkerScripthashesResponse>?> subscribeForStatuses([
|
||||||
List<BitcoinAddressRecord>? addresses,
|
List<BitcoinAddressRecord>? addresses,
|
||||||
bool? wait,
|
bool? wait,
|
||||||
]) async {
|
]) async {
|
||||||
|
@ -1686,6 +1694,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
addressByScripthashes[addressRecord.scriptHash] = addressRecord.address;
|
addressByScripthashes[addressRecord.scriptHash] = addressRecord.address;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (wait == true)
|
||||||
return ElectrumWorkerScripthashesSubscribeResponse.fromJson(
|
return ElectrumWorkerScripthashesSubscribeResponse.fromJson(
|
||||||
await waitSendWorker(
|
await waitSendWorker(
|
||||||
ElectrumWorkerScripthashesSubscribeRequest(
|
ElectrumWorkerScripthashesSubscribeRequest(
|
||||||
|
@ -1694,6 +1703,15 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).result;
|
).result;
|
||||||
|
else
|
||||||
|
sendWorker(
|
||||||
|
ElectrumWorkerScripthashesSubscribeRequest(
|
||||||
|
scripthashByAddress: scripthashByAddress,
|
||||||
|
addressByScripthashes: addressByScripthashes,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -1823,9 +1841,6 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: only once
|
|
||||||
initAddresses();
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ConnectionStatus.disconnected:
|
case ConnectionStatus.disconnected:
|
||||||
if (syncStatus is! NotConnectedSyncStatus &&
|
if (syncStatus is! NotConnectedSyncStatus &&
|
||||||
|
@ -1933,29 +1948,69 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> _onAddressesDiscovered(List<BitcoinAddressRecord> addresses) async {
|
Future<void> _onAddressesDiscovered(List<BitcoinAddressRecord> addresses) async {
|
||||||
try {
|
|
||||||
final scripthashByAddress = await subscribeForStatuses(addresses, true);
|
final scripthashByAddress = await subscribeForStatuses(addresses, true);
|
||||||
|
final noItemsWithStatus =
|
||||||
|
scripthashByAddress!.length == 1 && scripthashByAddress.first.status == null;
|
||||||
|
|
||||||
print("addresses: ${addresses.first.address}");
|
final firstAddress = addresses.first;
|
||||||
|
|
||||||
if (addresses.first.seedBytesType!.isOldDerivation && scripthashByAddress.length == 1) {
|
// NOTE: Did not find any status for old/wrong derivation addresses, discard them
|
||||||
// Wrong derivation address with no history, discard and do not add to wallet addresses
|
// (don't add to addresses list)
|
||||||
// Was only used to find transactions if any
|
if (firstAddress.seedBytesType!.isOldDerivation && noItemsWithStatus) {
|
||||||
await save();
|
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||||
|
addressType: firstAddress.type,
|
||||||
|
seedBytesType: firstAddress.seedBytesType!,
|
||||||
|
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||||
|
isChange: firstAddress.isChange,
|
||||||
|
discovered: true,
|
||||||
|
);
|
||||||
|
initAddresses();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
walletAddresses.addAddresses(addresses);
|
walletAddresses.addAddresses(addresses);
|
||||||
await syncAddresses(addresses);
|
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||||
} catch (_) {}
|
addressType: firstAddress.type,
|
||||||
|
seedBytesType: firstAddress.seedBytesType!,
|
||||||
|
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||||
|
isChange: firstAddress.isChange,
|
||||||
|
discovered: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// NOTE: Has items with status under gap limit, continue discovering
|
||||||
|
if (!noItemsWithStatus)
|
||||||
|
discoverNewAddresses(
|
||||||
|
seedBytesType: firstAddress.seedBytesType!,
|
||||||
|
isChange: firstAddress.isChange,
|
||||||
|
addressType: firstAddress.type,
|
||||||
|
derivationInfo: firstAddress.derivationInfo,
|
||||||
|
scripthashStatuses: scripthashByAddress,
|
||||||
|
);
|
||||||
|
// NOTE: Otherwise, sync all the discovered addresses so far
|
||||||
|
else {
|
||||||
|
subscribeForStatuses(
|
||||||
|
walletAddresses.addressesRecords
|
||||||
|
.getRecords(
|
||||||
|
seedBytesType: firstAddress.seedBytesType!,
|
||||||
|
addressType: firstAddress.type,
|
||||||
|
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||||
|
isChange: firstAddress.isChange,
|
||||||
|
)
|
||||||
|
.whereType<BitcoinAddressRecord>()
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
initAddresses();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void generateInitialAddresses({
|
Future<bool> generateInitialAddresses({
|
||||||
required BitcoinAddressType addressType,
|
required BitcoinAddressType addressType,
|
||||||
required SeedBytesType seedBytesType,
|
required SeedBytesType seedBytesType,
|
||||||
BitcoinDerivationInfo? bitcoinDerivationInfo,
|
BitcoinDerivationInfo? bitcoinDerivationInfo,
|
||||||
}) {
|
}) async {
|
||||||
|
bool discovered = false;
|
||||||
|
|
||||||
bitcoinDerivationInfo ??= BitcoinAddressUtils.getDerivationFromType(
|
bitcoinDerivationInfo ??= BitcoinAddressUtils.getDerivationFromType(
|
||||||
addressType,
|
addressType,
|
||||||
network: network,
|
network: network,
|
||||||
|
@ -1968,15 +2023,32 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||||
isChange: false,
|
isChange: false,
|
||||||
);
|
);
|
||||||
|
final discoveredExistingReceiveAddresses =
|
||||||
if (existingReceiveAddresses.length < ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT) {
|
walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||||
discoverNewAddresses(
|
|
||||||
seedBytesType: seedBytesType,
|
|
||||||
isChange: false,
|
|
||||||
addressType: addressType,
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
|
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||||
|
isChange: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!discoveredExistingReceiveAddresses &&
|
||||||
|
existingReceiveAddresses.length < ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT) {
|
||||||
|
discoverNewAddresses(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
derivationInfo: bitcoinDerivationInfo,
|
derivationInfo: bitcoinDerivationInfo,
|
||||||
|
isChange: false,
|
||||||
startIndex: existingReceiveAddresses.length,
|
startIndex: existingReceiveAddresses.length,
|
||||||
);
|
);
|
||||||
|
discovered = true;
|
||||||
|
} else {
|
||||||
|
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
|
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||||
|
isChange: false,
|
||||||
|
discovered: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final existingChangeAddresses = walletAddresses.addressesRecords.getRecords(
|
final existingChangeAddresses = walletAddresses.addressesRecords.getRecords(
|
||||||
|
@ -1985,16 +2057,35 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||||
isChange: true,
|
isChange: true,
|
||||||
);
|
);
|
||||||
|
final discoveredExistingChangeAddresses =
|
||||||
if (existingChangeAddresses.length < ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT) {
|
walletAddresses.discoveredAddressesRecord.getIsDiscovered(
|
||||||
discoverNewAddresses(
|
|
||||||
seedBytesType: seedBytesType,
|
|
||||||
isChange: true,
|
|
||||||
addressType: addressType,
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
|
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||||
|
isChange: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!discoveredExistingChangeAddresses &&
|
||||||
|
existingChangeAddresses.length < ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT) {
|
||||||
|
discoverNewAddresses(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
derivationInfo: bitcoinDerivationInfo,
|
derivationInfo: bitcoinDerivationInfo,
|
||||||
|
isChange: true,
|
||||||
startIndex: existingChangeAddresses.length,
|
startIndex: existingChangeAddresses.length,
|
||||||
);
|
);
|
||||||
|
discovered = true;
|
||||||
|
} else {
|
||||||
|
walletAddresses.discoveredAddressesRecord.addDiscovered(
|
||||||
|
addressType: addressType,
|
||||||
|
seedBytesType: seedBytesType,
|
||||||
|
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||||
|
isChange: true,
|
||||||
|
discovered: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return discovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
void discoverNewAddresses({
|
void discoverNewAddresses({
|
||||||
|
@ -2003,8 +2094,9 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
required BitcoinAddressType addressType,
|
required BitcoinAddressType addressType,
|
||||||
required BitcoinDerivationInfo derivationInfo,
|
required BitcoinDerivationInfo derivationInfo,
|
||||||
int? startIndex,
|
int? startIndex,
|
||||||
|
List<ElectrumWorkerScripthashesResponse>? scripthashStatuses,
|
||||||
}) async {
|
}) async {
|
||||||
final count = isChange
|
final countToDiscover = isChange
|
||||||
? ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT
|
? ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT
|
||||||
: ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT;
|
: ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT;
|
||||||
|
|
||||||
|
@ -2018,19 +2110,20 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
startIndex ??= recordList.length;
|
startIndex ??= recordList.length;
|
||||||
|
|
||||||
late bool needsToDiscover;
|
late bool needsToDiscover;
|
||||||
print(addressType);
|
|
||||||
print(seedBytesType);
|
|
||||||
print(derivationInfo.derivationPath.toString());
|
|
||||||
print(isChange);
|
|
||||||
print([addressType, recordList.length]);
|
|
||||||
|
|
||||||
if (recordList.length < count) {
|
if (recordList.length < countToDiscover) {
|
||||||
needsToDiscover = true;
|
needsToDiscover = true;
|
||||||
} else if (recordList.length == count) {
|
} else if (recordList.length == countToDiscover) {
|
||||||
needsToDiscover = recordList.any((record) => !record.getIsUsed());
|
needsToDiscover = recordList.any((record) => !record.getIsUsed());
|
||||||
} else {
|
} else {
|
||||||
needsToDiscover = recordList.sublist(recordList.length - count).any(
|
needsToDiscover = recordList.sublist(recordList.length - countToDiscover).any(
|
||||||
(record) => record.getIsUsed(),
|
(record) {
|
||||||
|
return scripthashStatuses?.any(
|
||||||
|
(scripthashStatus) =>
|
||||||
|
scripthashStatus.scripthash == (record as BitcoinAddressRecord).scriptHash,
|
||||||
|
) ??
|
||||||
|
record.getIsUsed();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2041,7 +2134,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
||||||
workerSendPort!.send(
|
workerSendPort!.send(
|
||||||
ElectrumWorkerDiscoverAddressesRequest(
|
ElectrumWorkerDiscoverAddressesRequest(
|
||||||
id: _messageId,
|
id: _messageId,
|
||||||
count: count,
|
count: countToDiscover,
|
||||||
walletType: type,
|
walletType: type,
|
||||||
startIndex: startIndex,
|
startIndex: startIndex,
|
||||||
seedBytesType: seedBytesType,
|
seedBytesType: seedBytesType,
|
||||||
|
|
|
@ -28,7 +28,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
BitcoinAddressType? initialAddressPageType,
|
BitcoinAddressType? initialAddressPageType,
|
||||||
}) : addressesRecords = initialAddressesRecords ?? BitcoinAddressRecordMap(),
|
}) : addressesRecords = initialAddressesRecords ?? BitcoinAddressRecordMap(),
|
||||||
activeIndexByType = initialActiveAddressIndex ?? {},
|
activeIndexByType = initialActiveAddressIndex ?? {},
|
||||||
discoveredAddressTypes = initialDiscoveredAddresses ?? BitcoinDiscoveredAddressesMap(),
|
discoveredAddressesRecord = initialDiscoveredAddresses ?? BitcoinDiscoveredAddressesMap(),
|
||||||
addressPageType = initialAddressPageType ??
|
addressPageType = initialAddressPageType ??
|
||||||
(walletInfo.addressPageType != null
|
(walletInfo.addressPageType != null
|
||||||
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
||||||
|
@ -49,6 +49,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
String get xpub => hdWallet.publicKey.toExtended;
|
String get xpub => hdWallet.publicKey.toExtended;
|
||||||
String get xpriv => hdWallet.privateKey.toExtended;
|
String get xpriv => hdWallet.privateKey.toExtended;
|
||||||
|
|
||||||
|
// NOTE: order matters in priority
|
||||||
|
List<SeedBytesType> get seedBytesTypes {
|
||||||
|
final seedBytesTypes = <SeedBytesType>[];
|
||||||
|
if (hdWallets.containsKey(SeedBytesType.bip39)) {
|
||||||
|
seedBytesTypes.add(SeedBytesType.bip39);
|
||||||
|
}
|
||||||
|
if (hdWallets.containsKey(SeedBytesType.electrum)) {
|
||||||
|
seedBytesTypes.add(SeedBytesType.electrum);
|
||||||
|
}
|
||||||
|
return seedBytesTypes;
|
||||||
|
}
|
||||||
|
|
||||||
SeedBytesType get walletSeedBytesType => hdWallets.containsKey(SeedBytesType.bip39)
|
SeedBytesType get walletSeedBytesType => hdWallets.containsKey(SeedBytesType.bip39)
|
||||||
? SeedBytesType.bip39
|
? SeedBytesType.bip39
|
||||||
: (hdWallets.containsKey(SeedBytesType.electrum)
|
: (hdWallets.containsKey(SeedBytesType.electrum)
|
||||||
|
@ -83,11 +95,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
bool isEnabledAutoGenerateNewAddress = true;
|
bool isEnabledAutoGenerateNewAddress = true;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
BitcoinDiscoveredAddressesMap discoveredAddressTypes;
|
BitcoinDiscoveredAddressesMap discoveredAddressesRecord;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
BitcoinAddressRecordMap addressesRecords;
|
BitcoinAddressRecordMap addressesRecords;
|
||||||
|
|
||||||
|
Set<String> get hiddenAddresses =>
|
||||||
|
allAddresses.where((address) => address.isHidden).map((address) => address.address).toSet();
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
List<BitcoinAddressRecord> _allAddresses = [];
|
List<BitcoinAddressRecord> _allAddresses = [];
|
||||||
@computed
|
@computed
|
||||||
|
@ -257,12 +272,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void resetActiveAddress() {
|
void resetActiveAddress() {
|
||||||
|
try {
|
||||||
final activeReceiveAddresses = selectedReceiveAddresses.whereType<BitcoinAddressRecord>();
|
final activeReceiveAddresses = selectedReceiveAddresses.whereType<BitcoinAddressRecord>();
|
||||||
|
|
||||||
activeBitcoinAddress = activeReceiveAddresses.firstWhereOrNull(
|
activeBitcoinAddress = activeReceiveAddresses.firstWhereOrNull(
|
||||||
(addressRecord) => addressRecord.index == activeIndexByType[addressPageType],
|
(addressRecord) => addressRecord.index == activeIndexByType[addressPageType],
|
||||||
) ??
|
) ??
|
||||||
activeReceiveAddresses.first;
|
activeReceiveAddresses.first;
|
||||||
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -272,11 +289,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
return activeBitcoinAddress!.address;
|
return activeBitcoinAddress!.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
final receiveAddress = selectedReceiveAddresses
|
final receiveAddress = nextReceiveAddress?.address;
|
||||||
.firstWhereOrNull(
|
|
||||||
(addr) => addr.getIsStillReceiveable(isEnabledAutoGenerateNewAddress) && !addr.isChange,
|
|
||||||
)
|
|
||||||
?.address;
|
|
||||||
|
|
||||||
return receiveAddress ?? '';
|
return receiveAddress ?? '';
|
||||||
}
|
}
|
||||||
|
@ -442,22 +455,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
void updateAddresses(Iterable<BitcoinAddressRecord> newAddresses) {
|
|
||||||
final replacedAddresses = newAddresses.toList();
|
|
||||||
for (final address in newAddresses) {
|
|
||||||
final index = _allAddresses.indexWhere((element) => element.address == address.address);
|
|
||||||
if (index >= 0) {
|
|
||||||
_allAddresses.replaceRange(index, index + 1, [address]);
|
|
||||||
replacedAddresses.remove(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (replacedAddresses.isNotEmpty) {
|
|
||||||
_allAddresses.addAll(replacedAddresses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void addAddresses(List<BitcoinAddressRecord> addresses) {
|
void addAddresses(List<BitcoinAddressRecord> addresses) {
|
||||||
final firstAddress = addresses.first;
|
final firstAddress = addresses.first;
|
||||||
|
@ -469,28 +466,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
isChange: firstAddress.isChange,
|
isChange: firstAddress.isChange,
|
||||||
addressRecords: addresses,
|
addressRecords: addresses,
|
||||||
);
|
);
|
||||||
print("addressesRecords.allRecords().length");
|
|
||||||
print(firstAddress.type);
|
|
||||||
print(firstAddress.seedBytesType!);
|
|
||||||
print(firstAddress.derivationInfo.derivationPath.toString());
|
|
||||||
print(firstAddress.isChange);
|
|
||||||
|
|
||||||
print(addressesRecords
|
|
||||||
.getRecords(
|
|
||||||
addressType: firstAddress.type,
|
|
||||||
seedBytesType: firstAddress.seedBytesType!,
|
|
||||||
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
|
||||||
isChange: firstAddress.isChange,
|
|
||||||
)
|
|
||||||
.length);
|
|
||||||
|
|
||||||
updateAllAddresses();
|
updateAllAddresses();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
json['addressesRecords'] = addressesRecords.toJson();
|
json['addressesRecords'] = addressesRecords.toJson();
|
||||||
json['discoveredAddressTypes'] = discoveredAddressTypes.toJson();
|
json['discoveredAddressesRecord'] = discoveredAddressesRecord.toJson();
|
||||||
json['addressPageType'] = addressPageType.toString();
|
json['addressPageType'] = addressPageType.toString();
|
||||||
json['activeIndexByType'] = activeIndexByType.map(
|
json['activeIndexByType'] = activeIndexByType.map(
|
||||||
(key, value) => MapEntry(key.toString(), value),
|
(key, value) => MapEntry(key.toString(), value),
|
||||||
|
@ -508,7 +490,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
return {
|
return {
|
||||||
'allAddresses': addresses,
|
'allAddresses': addresses,
|
||||||
'addressesRecords': data['addressesRecords'] as Map<String, dynamic>?,
|
'addressesRecords': data['addressesRecords'] as Map<String, dynamic>?,
|
||||||
'discoveredAddressTypes': data['discoveredAddressTypes'] as Map<String, dynamic>?,
|
'discoveredAddressesRecord': data['discoveredAddressesRecord'] as Map<String, dynamic>?,
|
||||||
'addressPageType': data['addressPageType'] as String?,
|
'addressPageType': data['addressPageType'] as String?,
|
||||||
'activeIndexByType': (data['activeIndexByType'] as Map<dynamic, dynamic>?) ?? {},
|
'activeIndexByType': (data['activeIndexByType'] as Map<dynamic, dynamic>?) ?? {},
|
||||||
};
|
};
|
||||||
|
@ -529,9 +511,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
json['addressesRecords'] as Map<String, dynamic>,
|
json['addressesRecords'] as Map<String, dynamic>,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (json['discoveredAddressTypes'] != null)
|
if (json['discoveredAddressesRecord'] != null)
|
||||||
initialDiscoveredAddresses ??= BitcoinDiscoveredAddressesMap.fromJson(
|
initialDiscoveredAddresses ??= BitcoinDiscoveredAddressesMap.fromJson(
|
||||||
json['discoveredAddressTypes'] as Map<String, dynamic>,
|
json['discoveredAddressesRecord'] as Map<String, dynamic>,
|
||||||
);
|
);
|
||||||
|
|
||||||
return ElectrumWalletAddresses(
|
return ElectrumWalletAddresses(
|
||||||
|
@ -612,7 +594,24 @@ class BitcoinAddressRecordMap extends ItemsRecordMap<AddressRecordsBySeedType> {
|
||||||
);
|
);
|
||||||
_data[addressType]![seedBytesType]![derivationPath]!.putIfAbsent(isChange, () => []);
|
_data[addressType]![seedBytesType]![derivationPath]!.putIfAbsent(isChange, () => []);
|
||||||
|
|
||||||
_data[addressType]![seedBytesType]![derivationPath]![isChange]!.addAll(addressRecords);
|
final recordsList = _data[addressType]![seedBytesType]![derivationPath]![isChange]!;
|
||||||
|
|
||||||
|
if (recordsList.isEmpty) {
|
||||||
|
recordsList.addAll(addressRecords);
|
||||||
|
} else {
|
||||||
|
for (final addressRecord in addressRecords) {
|
||||||
|
final existingRecordIndex =
|
||||||
|
recordsList.indexWhere((record) => record.address == addressRecord.address);
|
||||||
|
|
||||||
|
if (existingRecordIndex >= 0) {
|
||||||
|
recordsList.replaceRange(existingRecordIndex, existingRecordIndex + 1, [addressRecord]);
|
||||||
|
} else {
|
||||||
|
recordsList.add(addressRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_data[addressType]![seedBytesType]![derivationPath]![isChange] = recordsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BaseBitcoinAddressRecord> allRecords() {
|
List<BaseBitcoinAddressRecord> allRecords() {
|
||||||
|
@ -697,31 +696,30 @@ class BitcoinDiscoveredAddressesMap extends ItemsRecordMap<DiscoveredAddressReco
|
||||||
required BitcoinAddressType addressType,
|
required BitcoinAddressType addressType,
|
||||||
required SeedBytesType seedBytesType,
|
required SeedBytesType seedBytesType,
|
||||||
required String derivationPath,
|
required String derivationPath,
|
||||||
required BaseBitcoinAddressRecord addressRecord,
|
required bool isChange,
|
||||||
required bool discovered,
|
required bool discovered,
|
||||||
}) {
|
}) {
|
||||||
_data.putIfAbsent(
|
_data.putIfAbsent(
|
||||||
addressType,
|
addressType,
|
||||||
() => {
|
() => {
|
||||||
seedBytesType: {
|
seedBytesType: {
|
||||||
derivationPath: {addressRecord.isChange: discovered},
|
derivationPath: {isChange: discovered},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
_data[addressType]!.putIfAbsent(
|
_data[addressType]!.putIfAbsent(
|
||||||
seedBytesType,
|
seedBytesType,
|
||||||
() => {
|
() => {
|
||||||
derivationPath: {addressRecord.isChange: discovered},
|
derivationPath: {isChange: discovered},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
_data[addressType]![seedBytesType]!.putIfAbsent(
|
_data[addressType]![seedBytesType]!.putIfAbsent(
|
||||||
derivationPath,
|
derivationPath,
|
||||||
() => {addressRecord.isChange: discovered},
|
() => {isChange: discovered},
|
||||||
);
|
);
|
||||||
_data[addressType]![seedBytesType]![derivationPath]!
|
_data[addressType]![seedBytesType]![derivationPath]!.putIfAbsent(isChange, () => discovered);
|
||||||
.putIfAbsent(addressRecord.isChange, () => discovered);
|
|
||||||
|
|
||||||
_data[addressType]![seedBytesType]![derivationPath]![addressRecord.isChange] = discovered;
|
_data[addressType]![seedBytesType]![derivationPath]![isChange] = discovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getIsDiscovered({
|
bool getIsDiscovered({
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||||
|
|
|
@ -65,8 +65,8 @@ class ElectrumWorkerScripthashesResponse {
|
||||||
|
|
||||||
static ElectrumWorkerScripthashesResponse fromJson(Map<String, dynamic> json) {
|
static ElectrumWorkerScripthashesResponse fromJson(Map<String, dynamic> json) {
|
||||||
return ElectrumWorkerScripthashesResponse(
|
return ElectrumWorkerScripthashesResponse(
|
||||||
address: json['address'] as String,
|
address: json['address'] as String? ?? '',
|
||||||
scripthash: json['scripthash'] as String,
|
scripthash: json['scripthash'] as String? ?? '',
|
||||||
status: json['status'] as String?,
|
status: json['status'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -89,9 +89,17 @@ class ElectrumWorkerScripthashesSubscribeResponse extends ElectrumWorkerResponse
|
||||||
@override
|
@override
|
||||||
factory ElectrumWorkerScripthashesSubscribeResponse.fromJson(Map<String, dynamic> json) {
|
factory ElectrumWorkerScripthashesSubscribeResponse.fromJson(Map<String, dynamic> json) {
|
||||||
return ElectrumWorkerScripthashesSubscribeResponse(
|
return ElectrumWorkerScripthashesSubscribeResponse(
|
||||||
result: (json['result'] as List<dynamic>)
|
result: (json['result'] as List<dynamic>).map((e) {
|
||||||
.map((e) => ElectrumWorkerScripthashesResponse.fromJson(e as Map<String, dynamic>))
|
if (e is String) {
|
||||||
.toList(),
|
return ElectrumWorkerScripthashesResponse.fromJson(
|
||||||
|
jsonDecode(e) as Map<String, dynamic>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElectrumWorkerScripthashesResponse.fromJson(
|
||||||
|
e as Map<String, dynamic>,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
error: json['error'] as String?,
|
error: json['error'] as String?,
|
||||||
id: json['id'] as int?,
|
id: json['id'] as int?,
|
||||||
completed: json['completed'] as bool? ?? false,
|
completed: json['completed'] as bool? ?? false,
|
||||||
|
|
|
@ -66,13 +66,6 @@ class TransactionsPage extends StatelessWidget {
|
||||||
dashboardViewModel: dashboardViewModel,
|
dashboardViewModel: dashboardViewModel,
|
||||||
key: ValueKey('transactions_page_header_row_key'),
|
key: ValueKey('transactions_page_header_row_key'),
|
||||||
),
|
),
|
||||||
Center(
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
backgroundColor: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
|
||||||
Theme.of(context).extension<ExchangePageTheme>()!.firstGradientBottomPanelColor,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue