mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 12:09:43 +00:00
fix: scan fixes, add date, allow sending while scanning
This commit is contained in:
parent
b7c942ac4e
commit
8e5d997562
9 changed files with 190 additions and 214 deletions
|
@ -37,6 +37,7 @@ import 'package:cw_core/utils/file.dart';
|
|||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/get_height_by_date.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
@ -359,7 +360,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
syncStatus = message.syncStatus;
|
||||
walletInfo.restoreHeight = message.height;
|
||||
await walletInfo.updateRestoreHeight(message.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,6 +382,8 @@ abstract class ElectrumWalletBase
|
|||
await updateBalance();
|
||||
|
||||
await updateFeeRates();
|
||||
|
||||
syncStatus = SyncedSyncStatus();
|
||||
} catch (e, stacktrace) {
|
||||
print(stacktrace);
|
||||
print(e.toString());
|
||||
|
@ -1141,7 +1144,8 @@ abstract class ElectrumWalletBase
|
|||
coin.isFrozen = coinInfo.isFrozen;
|
||||
coin.isSending = coinInfo.isSending;
|
||||
coin.note = coinInfo.note;
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
} else {
|
||||
_addCoinInfo(coin);
|
||||
}
|
||||
|
@ -1172,7 +1176,8 @@ abstract class ElectrumWalletBase
|
|||
coin.isFrozen = coinInfo.isFrozen;
|
||||
coin.isSending = coinInfo.isSending;
|
||||
coin.note = coinInfo.note;
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||
} else {
|
||||
_addCoinInfo(coin);
|
||||
}
|
||||
|
@ -1588,7 +1593,6 @@ abstract class ElectrumWalletBase
|
|||
Future<void> updateTransactions() async {
|
||||
try {
|
||||
if (_isTransactionUpdating) {
|
||||
_isTransactionUpdating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1734,7 +1738,7 @@ abstract class ElectrumWalletBase
|
|||
_currentChainTip = height;
|
||||
|
||||
if (_currentChainTip != null && _currentChainTip! > 0 && walletInfo.restoreHeight == 0) {
|
||||
walletInfo.restoreHeight = _currentChainTip!;
|
||||
await walletInfo.updateRestoreHeight(_currentChainTip!);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1818,202 +1822,156 @@ class SyncResponse {
|
|||
}
|
||||
|
||||
Future<void> startRefresh(ScanData scanData) async {
|
||||
Future<ElectrumClient> getElectrumConnection() async {
|
||||
final electrumClient = scanData.electrumClient;
|
||||
if (!electrumClient.isConnected) {
|
||||
final node = scanData.node;
|
||||
await electrumClient.connectToUri(node.uri, useSSL: node.useSSL);
|
||||
}
|
||||
return electrumClient;
|
||||
}
|
||||
|
||||
var lastKnownBlockHeight = 0;
|
||||
var initialSyncHeight = 0;
|
||||
|
||||
var syncHeight = scanData.height;
|
||||
var currentChainTip = scanData.chainTip;
|
||||
|
||||
if (syncHeight <= 0) {
|
||||
syncHeight = currentChainTip;
|
||||
}
|
||||
|
||||
if (initialSyncHeight <= 0) {
|
||||
initialSyncHeight = syncHeight;
|
||||
}
|
||||
|
||||
if (lastKnownBlockHeight == syncHeight) {
|
||||
scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus()));
|
||||
return;
|
||||
}
|
||||
int syncHeight = scanData.height;
|
||||
int initialSyncHeight = syncHeight;
|
||||
|
||||
BehaviorSubject<Object>? tweaksSubscription = null;
|
||||
|
||||
lastKnownBlockHeight = syncHeight;
|
||||
|
||||
SyncingSyncStatus syncingStatus;
|
||||
if (scanData.isSingleScan) {
|
||||
syncingStatus = SyncingSyncStatus(1, 0);
|
||||
} else {
|
||||
syncingStatus =
|
||||
SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight);
|
||||
}
|
||||
final syncingStatus = scanData.isSingleScan
|
||||
? SyncingSyncStatus(1, 0)
|
||||
: SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, syncHeight);
|
||||
|
||||
// Initial status UI update, send how many blocks left to scan
|
||||
scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
|
||||
|
||||
if (syncingStatus.blocksLeft <= 0 || (scanData.isSingleScan && scanData.height != syncHeight)) {
|
||||
scanData.sendPort.send(SyncResponse(scanData.chainTip, SyncedSyncStatus()));
|
||||
return;
|
||||
}
|
||||
final electrumClient = scanData.electrumClient;
|
||||
await electrumClient.connectToUri(scanData.node.uri, useSSL: scanData.node.useSSL);
|
||||
|
||||
try {
|
||||
final electrumClient = await getElectrumConnection();
|
||||
if (tweaksSubscription == null) {
|
||||
final count = scanData.isSingleScan ? 1 : TWEAKS_COUNT;
|
||||
final receiver = Receiver(
|
||||
scanData.silentAddress.b_scan.toHex(),
|
||||
scanData.silentAddress.B_spend.toHex(),
|
||||
scanData.network == BitcoinNetwork.testnet,
|
||||
scanData.labelIndexes,
|
||||
scanData.labelIndexes.length,
|
||||
);
|
||||
|
||||
if (tweaksSubscription == null) {
|
||||
final count = scanData.isSingleScan ? 1 : TWEAKS_COUNT;
|
||||
tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight, count: count);
|
||||
tweaksSubscription?.listen((t) async {
|
||||
final tweaks = t as Map<String, dynamic>;
|
||||
|
||||
if (tweaks["message"] != null) {
|
||||
// re-subscribe to continue receiving messages
|
||||
electrumClient.tweaksSubscribe(height: syncHeight, count: count);
|
||||
return;
|
||||
}
|
||||
|
||||
final blockHeight = tweaks.keys.first;
|
||||
final tweakHeight = int.parse(blockHeight);
|
||||
|
||||
try {
|
||||
tweaksSubscription = await electrumClient.tweaksSubscribe(height: syncHeight, count: count);
|
||||
final blockTweaks = tweaks[blockHeight] as Map<String, dynamic>;
|
||||
|
||||
tweaksSubscription?.listen((t) async {
|
||||
final tweaks = t as Map<String, dynamic>;
|
||||
|
||||
if (tweaks["message"] != null && !scanData.isSingleScan) {
|
||||
// re-subscribe to continue receiving messages
|
||||
electrumClient.tweaksSubscribe(height: syncHeight, count: count);
|
||||
return;
|
||||
}
|
||||
|
||||
final blockHeight = tweaks.keys.first;
|
||||
final tweakHeight = int.parse(blockHeight);
|
||||
for (var j = 0; j < blockTweaks.keys.length; j++) {
|
||||
final txid = blockTweaks.keys.elementAt(j);
|
||||
final details = blockTweaks[txid] as Map<String, dynamic>;
|
||||
final outputPubkeys = (details["output_pubkeys"] as Map<dynamic, dynamic>);
|
||||
final tweak = details["tweak"].toString();
|
||||
|
||||
try {
|
||||
final blockTweaks = tweaks[blockHeight] as Map<String, dynamic>;
|
||||
// scanOutputs called from rust here
|
||||
final addToWallet = scanOutputs(
|
||||
outputPubkeys.values.toList(),
|
||||
tweak,
|
||||
receiver,
|
||||
);
|
||||
|
||||
for (var j = 0; j < blockTweaks.keys.length; j++) {
|
||||
final txid = blockTweaks.keys.elementAt(j);
|
||||
final details = blockTweaks[txid] as Map<String, dynamic>;
|
||||
final outputPubkeys = (details["output_pubkeys"] as Map<dynamic, dynamic>);
|
||||
final tweak = details["tweak"].toString();
|
||||
if (addToWallet.isEmpty) {
|
||||
// no results tx, continue to next tx
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// scanOutputs called from rust here
|
||||
final addToWallet = scanOutputs(
|
||||
outputPubkeys.values.map((o) => o[0].toString()).toList(),
|
||||
tweak,
|
||||
Receiver(
|
||||
scanData.silentAddress.b_scan.toHex(),
|
||||
scanData.silentAddress.B_spend.toHex(),
|
||||
scanData.network == BitcoinNetwork.testnet,
|
||||
scanData.labelIndexes,
|
||||
scanData.labelIndexes.length,
|
||||
),
|
||||
);
|
||||
// placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s)
|
||||
final txInfo = ElectrumTransactionInfo(
|
||||
WalletType.bitcoin,
|
||||
id: txid,
|
||||
height: tweakHeight,
|
||||
amount: 0,
|
||||
fee: 0,
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: false,
|
||||
date: scanData.network == BitcoinNetwork.mainnet
|
||||
? getDateByBitcoinHeight(tweakHeight)
|
||||
: DateTime.now(),
|
||||
confirmations: scanData.chainTip - tweakHeight + 1,
|
||||
unspents: [],
|
||||
);
|
||||
|
||||
if (addToWallet.isEmpty) {
|
||||
// no results tx, continue to next tx
|
||||
continue;
|
||||
}
|
||||
addToWallet.forEach((label, value) {
|
||||
(value as Map<String, dynamic>).forEach((output, tweak) {
|
||||
final t_k = tweak.toString();
|
||||
|
||||
// placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s)
|
||||
final txInfo = ElectrumTransactionInfo(
|
||||
WalletType.bitcoin,
|
||||
id: txid,
|
||||
height: tweakHeight,
|
||||
amount: 0,
|
||||
fee: 0,
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: false,
|
||||
date: DateTime.now(),
|
||||
confirmations: scanData.chainTip - tweakHeight + 1,
|
||||
unspents: [],
|
||||
);
|
||||
final receivingOutputAddress = ECPublic.fromHex(output)
|
||||
.toTaprootAddress(tweak: false)
|
||||
.toAddress(scanData.network);
|
||||
|
||||
addToWallet.forEach((label, value) {
|
||||
(value as Map<String, dynamic>).forEach((output, tweak) {
|
||||
final t_k = tweak.toString();
|
||||
|
||||
final receivingOutputAddress = ECPublic.fromHex(output)
|
||||
.toTaprootAddress(tweak: false)
|
||||
.toAddress(scanData.network);
|
||||
|
||||
final receivedAddressRecord = BitcoinSilentPaymentAddressRecord(
|
||||
receivingOutputAddress,
|
||||
index: 0,
|
||||
isHidden: false,
|
||||
isUsed: true,
|
||||
network: scanData.network,
|
||||
silentPaymentTweak: t_k,
|
||||
type: SegwitAddresType.p2tr,
|
||||
txCount: 1,
|
||||
);
|
||||
|
||||
int? amount;
|
||||
int? pos;
|
||||
outputPubkeys.entries.firstWhere((k) {
|
||||
final isMatchingOutput = k.value[0] == output;
|
||||
if (isMatchingOutput) {
|
||||
amount = int.parse(k.value[1].toString());
|
||||
pos = int.parse(k.key.toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
final unspent = BitcoinSilentPaymentsUnspent(
|
||||
receivedAddressRecord,
|
||||
txid,
|
||||
amount!,
|
||||
pos!,
|
||||
silentPaymentTweak: t_k,
|
||||
silentPaymentLabel: label == "None" ? null : label,
|
||||
);
|
||||
|
||||
txInfo.unspents!.add(unspent);
|
||||
txInfo.amount += unspent.value;
|
||||
});
|
||||
int? amount;
|
||||
int? pos;
|
||||
outputPubkeys.entries.firstWhere((k) {
|
||||
final isMatchingOutput = k.value[0] == output;
|
||||
if (isMatchingOutput) {
|
||||
amount = int.parse(k.value[1].toString());
|
||||
pos = int.parse(k.key.toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
scanData.sendPort.send({txInfo.id: txInfo});
|
||||
} catch (_) {}
|
||||
}
|
||||
final receivedAddressRecord = BitcoinSilentPaymentAddressRecord(
|
||||
receivingOutputAddress,
|
||||
index: 0,
|
||||
isHidden: false,
|
||||
isUsed: true,
|
||||
network: scanData.network,
|
||||
silentPaymentTweak: t_k,
|
||||
type: SegwitAddresType.p2tr,
|
||||
txCount: 1,
|
||||
balance: amount!,
|
||||
);
|
||||
|
||||
final unspent = BitcoinSilentPaymentsUnspent(
|
||||
receivedAddressRecord,
|
||||
txid,
|
||||
amount!,
|
||||
pos!,
|
||||
silentPaymentTweak: t_k,
|
||||
silentPaymentLabel: label == "None" ? null : label,
|
||||
);
|
||||
|
||||
txInfo.unspents!.add(unspent);
|
||||
txInfo.amount += unspent.value;
|
||||
});
|
||||
});
|
||||
|
||||
scanData.sendPort.send({txInfo.id: txInfo});
|
||||
} catch (_) {}
|
||||
|
||||
syncHeight = tweakHeight;
|
||||
scanData.sendPort.send(
|
||||
SyncResponse(
|
||||
syncHeight,
|
||||
SyncingSyncStatus.fromHeightValues(
|
||||
currentChainTip,
|
||||
initialSyncHeight,
|
||||
syncHeight,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (int.parse(blockHeight) >= scanData.chainTip || scanData.isSingleScan) {
|
||||
scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus()));
|
||||
await tweaksSubscription!.close();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (e is RequestFailedTimeoutException) {
|
||||
return scanData.sendPort.send(
|
||||
SyncResponse(syncHeight, TimedOutSyncStatus()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
if (tweaksSubscription == null) {
|
||||
return scanData.sendPort.send(
|
||||
SyncResponse(syncHeight, UnsupportedSyncStatus()),
|
||||
syncHeight = tweakHeight;
|
||||
scanData.sendPort.send(
|
||||
SyncResponse(
|
||||
syncHeight,
|
||||
SyncingSyncStatus.fromHeightValues(
|
||||
scanData.chainTip,
|
||||
initialSyncHeight,
|
||||
syncHeight,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e, stacktrace) {
|
||||
print(stacktrace);
|
||||
print(e.toString());
|
||||
|
||||
scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus()));
|
||||
if (tweakHeight >= scanData.chainTip || scanData.isSingleScan) {
|
||||
scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus()));
|
||||
await tweaksSubscription!.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (tweaksSubscription == null) {
|
||||
return scanData.sendPort.send(
|
||||
SyncResponse(syncHeight, UnsupportedSyncStatus()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -794,7 +794,7 @@ packages:
|
|||
description:
|
||||
path: "."
|
||||
ref: master
|
||||
resolved-ref: a6b14bcc37ec16f56931e48afa8a8f8e6939431d
|
||||
resolved-ref: "4977a0d31fc8614d27193b07d92c5992d163131e"
|
||||
url: "https://github.com/rafael-xmr/sp_scanner"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
|
|
|
@ -245,6 +245,7 @@ Future<int> getHavenCurrentHeight() async {
|
|||
|
||||
// Data taken from https://timechaincalendar.com/
|
||||
const bitcoinDates = {
|
||||
"2024-05": 841590,
|
||||
"2024-04": 837182,
|
||||
"2024-03": 832623,
|
||||
"2024-02": 828319,
|
||||
|
@ -265,7 +266,9 @@ const bitcoinDates = {
|
|||
|
||||
int getBitcoinHeightByDate({required DateTime date}) {
|
||||
String dateKey = '${date.year}-${date.month.toString().padLeft(2, '0')}';
|
||||
int startBlock = bitcoinDates[dateKey] ?? bitcoinDates.values.last;
|
||||
final closestKey = bitcoinDates.keys
|
||||
.firstWhere((key) => formatMapKey(key).isBefore(date), orElse: () => bitcoinDates.keys.last);
|
||||
int startBlock = bitcoinDates[dateKey] ?? bitcoinDates[closestKey]!;
|
||||
|
||||
DateTime startOfMonth = DateTime(date.year, date.month);
|
||||
int daysDifference = date.difference(startOfMonth).inDays;
|
||||
|
@ -275,3 +278,9 @@ int getBitcoinHeightByDate({required DateTime date}) {
|
|||
|
||||
return startBlock + estimatedBlocksSinceStartOfMonth;
|
||||
}
|
||||
|
||||
DateTime getDateByBitcoinHeight(int height) {
|
||||
final date = bitcoinDates.entries
|
||||
.lastWhere((entry) => entry.value >= height, orElse: () => bitcoinDates.entries.last);
|
||||
return formatMapKey(date.key);
|
||||
}
|
||||
|
|
|
@ -66,21 +66,21 @@ class DerivationInfo extends HiveObject {
|
|||
@HiveType(typeId: WalletInfo.typeId)
|
||||
class WalletInfo extends HiveObject {
|
||||
WalletInfo(
|
||||
this.id,
|
||||
this.name,
|
||||
this.type,
|
||||
this.isRecovery,
|
||||
this.restoreHeight,
|
||||
this.timestamp,
|
||||
this.dirPath,
|
||||
this.path,
|
||||
this.address,
|
||||
this.yatEid,
|
||||
this.yatLastUsedAddressRaw,
|
||||
this.showIntroCakePayCard,
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
): _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
this.id,
|
||||
this.name,
|
||||
this.type,
|
||||
this.isRecovery,
|
||||
this.restoreHeight,
|
||||
this.timestamp,
|
||||
this.dirPath,
|
||||
this.path,
|
||||
this.address,
|
||||
this.yatEid,
|
||||
this.yatLastUsedAddressRaw,
|
||||
this.showIntroCakePayCard,
|
||||
this.derivationInfo,
|
||||
this.hardwareWalletType,
|
||||
) : _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
|
||||
factory WalletInfo.external({
|
||||
required String id,
|
||||
|
@ -207,4 +207,9 @@ class WalletInfo extends HiveObject {
|
|||
Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
|
||||
|
||||
StreamController<String> _yatLastUsedAddressController;
|
||||
|
||||
Future<void> updateRestoreHeight(int height) async {
|
||||
restoreHeight = height;
|
||||
await save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ const cakeWalletBitcoinCashDefaultNodeUri = 'bitcoincash.stackwallet.com:50002';
|
|||
const nanoDefaultNodeUri = 'rpc.nano.to';
|
||||
const nanoDefaultPowNodeUri = 'rpc.nano.to';
|
||||
const solanaDefaultNodeUri = 'rpc.ankr.com';
|
||||
const tronDefaultNodeUri = 'api.trongrid.io';
|
||||
const tronDefaultNodeUri = 'tron-rpc.publicnode.com:443';
|
||||
const newCakeWalletBitcoinUri = '198.58.111.154:50001';
|
||||
|
||||
|
|
|
@ -12,28 +12,27 @@ import 'package:wakelock_plus/wakelock_plus.dart';
|
|||
ReactionDisposer? _onWalletSyncStatusChangeReaction;
|
||||
|
||||
void startWalletSyncStatusChangeReaction(
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
|
||||
TransactionInfo> wallet,
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet,
|
||||
FiatConversionStore fiatConversionStore) {
|
||||
_onWalletSyncStatusChangeReaction?.reaction.dispose();
|
||||
_onWalletSyncStatusChangeReaction =
|
||||
reaction((_) => wallet.syncStatus, (SyncStatus status) async {
|
||||
try {
|
||||
if (status is ConnectedSyncStatus) {
|
||||
await wallet.startSync();
|
||||
_onWalletSyncStatusChangeReaction = reaction((_) => wallet.syncStatus, (SyncStatus status) async {
|
||||
if (!(status is SyncingSyncStatus) || wallet.type != WalletType.bitcoin)
|
||||
try {
|
||||
if (status is ConnectedSyncStatus) {
|
||||
await wallet.startSync();
|
||||
|
||||
if (wallet.type == WalletType.haven) {
|
||||
await updateHavenRate(fiatConversionStore);
|
||||
if (wallet.type == WalletType.haven) {
|
||||
await updateHavenRate(fiatConversionStore);
|
||||
}
|
||||
}
|
||||
if (status is SyncingSyncStatus) {
|
||||
await WakelockPlus.enable();
|
||||
}
|
||||
if (status is SyncedSyncStatus || status is FailedSyncStatus) {
|
||||
await WakelockPlus.disable();
|
||||
}
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
if (status is SyncingSyncStatus) {
|
||||
await WakelockPlus.enable();
|
||||
}
|
||||
if (status is SyncedSyncStatus || status is FailedSyncStatus) {
|
||||
await WakelockPlus.disable();
|
||||
}
|
||||
} catch(e) {
|
||||
print(e.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -252,6 +252,7 @@ class CryptoBalanceWidget extends StatelessWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: DashBoardRoundedCardWidget(
|
||||
customBorder: 30,
|
||||
title: S.of(context).silent_payments,
|
||||
subTitle: S.of(context).enable_silent_payments_scanning,
|
||||
hint: Column(
|
||||
|
|
|
@ -13,6 +13,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
|
|||
this.svgPicture,
|
||||
this.icon,
|
||||
this.onClose,
|
||||
this.customBorder,
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
|
@ -22,6 +23,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
|
|||
final Widget? hint;
|
||||
final SvgPicture? svgPicture;
|
||||
final Icon? icon;
|
||||
final double? customBorder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -37,7 +39,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
|
|||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderRadius: BorderRadius.circular(customBorder ?? 20),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
),
|
||||
|
|
|
@ -218,7 +218,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
|||
isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + ' ' + fiat.title;
|
||||
|
||||
@computed
|
||||
bool get isReadyForSend => wallet.syncStatus is SyncedSyncStatus;
|
||||
bool get isReadyForSend =>
|
||||
wallet.syncStatus is SyncedSyncStatus ||
|
||||
// If silent payments scanning, can still send payments
|
||||
(wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus);
|
||||
|
||||
@computed
|
||||
List<Template> get templates => sendTemplateViewModel.templates
|
||||
|
|
Loading…
Reference in a new issue