mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
feat: fix rescan & stop, new card
This commit is contained in:
parent
b7ff9ab32b
commit
e16a2180c4
41 changed files with 406 additions and 178 deletions
|
@ -35,6 +35,13 @@ part 'bitcoin_wallet.g.dart';
|
|||
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
||||
|
||||
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
@observable
|
||||
bool nodeSupportsSilentPayments = true;
|
||||
@observable
|
||||
bool silentPaymentsScanningActive = false;
|
||||
@observable
|
||||
bool allowedToSwitchNodesForScanning = false;
|
||||
|
||||
BitcoinWalletBase({
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
|
@ -307,63 +314,68 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
Future<bool> getNodeIsElectrs() async {
|
||||
if (node?.uri.host.contains("electrs") ?? false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final version = await sendWorker(ElectrumWorkerGetVersionRequest());
|
||||
|
||||
if (version is List<String> && version.isNotEmpty) {
|
||||
final server = version[0];
|
||||
|
||||
if (server.toLowerCase().contains('electrs')) {
|
||||
node!.isElectrs = true;
|
||||
node!.save();
|
||||
return node!.isElectrs!;
|
||||
}
|
||||
} else if (version is String && version.toLowerCase().contains('electrs')) {
|
||||
node!.isElectrs = true;
|
||||
node!.save();
|
||||
if (node?.isElectrs != null) {
|
||||
return node!.isElectrs!;
|
||||
}
|
||||
|
||||
node!.isElectrs = false;
|
||||
final isNamedElectrs = node?.uri.host.contains("electrs") ?? false;
|
||||
if (isNamedElectrs) {
|
||||
node!.isElectrs = true;
|
||||
}
|
||||
|
||||
final isNamedFulcrum = node!.uri.host.contains("fulcrum");
|
||||
if (isNamedFulcrum) {
|
||||
node!.isElectrs = false;
|
||||
}
|
||||
|
||||
if (node!.isElectrs == null) {
|
||||
final version = await sendWorker(ElectrumWorkerGetVersionRequest());
|
||||
|
||||
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')) {
|
||||
node!.isElectrs = true;
|
||||
} else {
|
||||
node!.isElectrs = false;
|
||||
}
|
||||
}
|
||||
|
||||
node!.save();
|
||||
return node!.isElectrs!;
|
||||
}
|
||||
|
||||
Future<bool> getNodeSupportsSilentPayments() async {
|
||||
// TODO: handle disconnection on check
|
||||
// TODO: use cached values
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final isFulcrum = node!.uri.host.contains("fulcrum");
|
||||
if (isFulcrum) {
|
||||
return false;
|
||||
if (node?.supportsSilentPayments != null) {
|
||||
return node!.supportsSilentPayments!;
|
||||
}
|
||||
|
||||
// As of today (august 2024), only ElectrumRS supports silent payments
|
||||
if (!(await getNodeIsElectrs())) {
|
||||
return false;
|
||||
final isElectrs = await getNodeIsElectrs();
|
||||
if (!isElectrs) {
|
||||
node!.supportsSilentPayments = false;
|
||||
}
|
||||
|
||||
try {
|
||||
final workerResponse = (await sendWorker(ElectrumWorkerCheckTweaksRequest())) as String;
|
||||
final tweaksResponse = ElectrumWorkerCheckTweaksResponse.fromJson(
|
||||
json.decode(workerResponse) as Map<String, dynamic>,
|
||||
);
|
||||
final supportsScanning = tweaksResponse.result == true;
|
||||
if (node!.supportsSilentPayments == null) {
|
||||
try {
|
||||
final workerResponse = (await sendWorker(ElectrumWorkerCheckTweaksRequest())) as String;
|
||||
final tweaksResponse = ElectrumWorkerCheckTweaksResponse.fromJson(
|
||||
json.decode(workerResponse) as Map<String, dynamic>,
|
||||
);
|
||||
final supportsScanning = tweaksResponse.result == true;
|
||||
|
||||
if (supportsScanning) {
|
||||
node!.supportsSilentPayments = true;
|
||||
node!.save();
|
||||
return node!.supportsSilentPayments!;
|
||||
if (supportsScanning) {
|
||||
node!.supportsSilentPayments = true;
|
||||
} else {
|
||||
node!.supportsSilentPayments = false;
|
||||
}
|
||||
} catch (_) {
|
||||
node!.supportsSilentPayments = false;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
node!.supportsSilentPayments = false;
|
||||
}
|
||||
node!.save();
|
||||
return node!.supportsSilentPayments!;
|
||||
}
|
||||
|
@ -437,8 +449,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
@action
|
||||
Future<void> setSilentPaymentsScanning(bool active) async {
|
||||
silentPaymentsScanningActive = active;
|
||||
final nodeSupportsSilentPayments = await getNodeSupportsSilentPayments();
|
||||
final isAllowedToScan = nodeSupportsSilentPayments || allowedToSwitchNodesForScanning;
|
||||
|
||||
if (active) {
|
||||
if (active && isAllowedToScan) {
|
||||
syncStatus = AttemptingScanSyncStatus();
|
||||
|
||||
final tip = currentChainTip!;
|
||||
|
@ -730,8 +744,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
await walletInfo.updateRestoreHeight(height);
|
||||
}
|
||||
}
|
||||
|
||||
await save();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -765,6 +777,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
.map((addr) => addr.labelIndex)
|
||||
.toList(),
|
||||
isSingleScan: doSingleScan ?? false,
|
||||
shouldSwitchNodes:
|
||||
!(await getNodeSupportsSilentPayments()) && allowedToSwitchNodesForScanning,
|
||||
),
|
||||
).toJson(),
|
||||
);
|
||||
|
|
|
@ -276,11 +276,6 @@ abstract class ElectrumWalletBase
|
|||
@override
|
||||
bool isTestnet;
|
||||
|
||||
@observable
|
||||
bool nodeSupportsSilentPayments = true;
|
||||
@observable
|
||||
bool silentPaymentsScanningActive = false;
|
||||
|
||||
bool _isTryingToConnect = false;
|
||||
|
||||
Completer<SharedPreferences> sharedPrefs = Completer();
|
||||
|
|
|
@ -14,17 +14,15 @@ import 'package:cw_bitcoin/electrum_worker/electrum_worker_methods.dart';
|
|||
import 'package:cw_bitcoin/electrum_worker/electrum_worker_params.dart';
|
||||
import 'package:cw_bitcoin/electrum_worker/methods/methods.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:sp_scanner/sp_scanner.dart';
|
||||
|
||||
class ElectrumWorker {
|
||||
final SendPort sendPort;
|
||||
ElectrumApiProvider? _electrumClient;
|
||||
BasedUtxoNetwork? _network;
|
||||
bool _isScanning = false;
|
||||
bool _stopScanRequested = false;
|
||||
BehaviorSubject<Map<String, dynamic>>? _scanningStream;
|
||||
|
||||
ElectrumWorker._(this.sendPort, {ElectrumApiProvider? electrumClient})
|
||||
: _electrumClient = electrumClient;
|
||||
|
@ -47,7 +45,7 @@ class ElectrumWorker {
|
|||
}
|
||||
|
||||
void handleMessage(dynamic message) async {
|
||||
print("Worker received message: $message");
|
||||
print("Worker: received message: $message");
|
||||
|
||||
try {
|
||||
Map<String, dynamic> messageJson;
|
||||
|
@ -105,27 +103,15 @@ class ElectrumWorker {
|
|||
);
|
||||
break;
|
||||
case ElectrumWorkerMethods.stopScanningMethod:
|
||||
print("Worker: received message: $message");
|
||||
await _handleStopScanning(
|
||||
ElectrumWorkerStopScanningRequest.fromJson(messageJson),
|
||||
);
|
||||
break;
|
||||
case ElectrumRequestMethods.tweaksSubscribeMethod:
|
||||
if (_isScanning) {
|
||||
_stopScanRequested = false;
|
||||
}
|
||||
|
||||
if (!_stopScanRequested) {
|
||||
await _handleScanSilentPayments(
|
||||
ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson),
|
||||
);
|
||||
} else {
|
||||
_stopScanRequested = false;
|
||||
_sendResponse(
|
||||
ElectrumWorkerTweaksSubscribeResponse(
|
||||
result: TweaksSyncResponse(syncStatus: SyncedSyncStatus()),
|
||||
),
|
||||
);
|
||||
}
|
||||
await _handleScanSilentPayments(
|
||||
ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson),
|
||||
);
|
||||
|
||||
break;
|
||||
case ElectrumRequestMethods.estimateFeeMethod:
|
||||
|
@ -550,28 +536,25 @@ class ElectrumWorker {
|
|||
}
|
||||
|
||||
Future<void> _handleStopScanning(ElectrumWorkerStopScanningRequest request) async {
|
||||
_stopScanRequested = true;
|
||||
_scanningStream?.close();
|
||||
_scanningStream = null;
|
||||
_sendResponse(
|
||||
ElectrumWorkerStopScanningResponse(result: true, id: request.id),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleScanSilentPayments(ElectrumWorkerTweaksSubscribeRequest request) async {
|
||||
_isScanning = true;
|
||||
final scanData = request.scanData;
|
||||
|
||||
// TODO: confirmedSwitch use new connection
|
||||
// final _electrumClient = await ElectrumApiProvider.connect(
|
||||
// ElectrumTCPService.connect(
|
||||
// Uri.parse("tcp://electrs.cakewallet.com:50001"),
|
||||
// onConnectionStatusChange: (status) {
|
||||
// _sendResponse(
|
||||
// ElectrumWorkerConnectionResponse(status: status, id: request.id),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
var scanningClient = _electrumClient;
|
||||
|
||||
if (scanData.shouldSwitchNodes) {
|
||||
scanningClient = await ElectrumApiProvider.connect(
|
||||
ElectrumTCPService.connect(
|
||||
Uri.parse("tcp://electrs.cakewallet.com:50001"),
|
||||
),
|
||||
);
|
||||
}
|
||||
int syncHeight = scanData.height;
|
||||
int initialSyncHeight = syncHeight;
|
||||
|
||||
|
@ -587,6 +570,15 @@ class ElectrumWorker {
|
|||
},
|
||||
);
|
||||
|
||||
int getCountPerRequest(int syncHeight) {
|
||||
if (scanData.isSingleScan) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
final amountLeft = scanData.chainTip - syncHeight + 1;
|
||||
return amountLeft;
|
||||
}
|
||||
|
||||
// Initial status UI update, send how many blocks in total to scan
|
||||
_sendResponse(ElectrumWorkerTweaksSubscribeResponse(
|
||||
result: TweaksSyncResponse(
|
||||
|
@ -597,17 +589,16 @@ class ElectrumWorker {
|
|||
|
||||
final req = ElectrumTweaksSubscribe(
|
||||
height: syncHeight,
|
||||
count: 1,
|
||||
count: getCountPerRequest(syncHeight),
|
||||
historicalMode: false,
|
||||
);
|
||||
|
||||
final stream = await _electrumClient!.subscribe(req);
|
||||
_scanningStream = await scanningClient!.subscribe(req);
|
||||
|
||||
void listenFn(Map<String, dynamic> event, ElectrumTweaksSubscribe req) {
|
||||
final response = req.onResponse(event);
|
||||
if (_stopScanRequested || response == null) {
|
||||
_stopScanRequested = false;
|
||||
_isScanning = false;
|
||||
|
||||
if (response == null || _scanningStream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -623,10 +614,10 @@ class ElectrumWorker {
|
|||
final nextHeight = syncHeight + 1;
|
||||
|
||||
if (nextHeight <= scanData.chainTip) {
|
||||
final nextStream = _electrumClient!.subscribe(
|
||||
final nextStream = scanningClient!.subscribe(
|
||||
ElectrumTweaksSubscribe(
|
||||
height: nextHeight,
|
||||
count: 1,
|
||||
count: getCountPerRequest(nextHeight),
|
||||
historicalMode: false,
|
||||
),
|
||||
);
|
||||
|
@ -710,6 +701,7 @@ class ElectrumWorker {
|
|||
receivingOutputAddress,
|
||||
labelIndex: 1, // TODO: get actual index/label
|
||||
isUsed: true,
|
||||
// TODO: use right wallet
|
||||
spendKey: scanData.silentPaymentsWallets.first.b_spend.tweakAdd(
|
||||
BigintUtils.fromBytes(BytesUtils.fromHexString(t_k)),
|
||||
),
|
||||
|
@ -753,24 +745,27 @@ class ElectrumWorker {
|
|||
),
|
||||
);
|
||||
|
||||
stream?.close();
|
||||
_scanningStream?.close();
|
||||
_scanningStream = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stream?.listen((event) => listenFn(event, req));
|
||||
_isScanning = false;
|
||||
_scanningStream?.listen((event) => listenFn(event, req));
|
||||
}
|
||||
|
||||
Future<void> _handleGetVersion(ElectrumWorkerGetVersionRequest request) async {
|
||||
_sendResponse(ElectrumWorkerGetVersionResponse(
|
||||
result: (await _electrumClient!.request(
|
||||
_sendResponse(
|
||||
ElectrumWorkerGetVersionResponse(
|
||||
result: await _electrumClient!.request(
|
||||
ElectrumVersion(
|
||||
clientName: "",
|
||||
protocolVersion: ["1.4"],
|
||||
protocolVersion: "1.4",
|
||||
),
|
||||
)),
|
||||
id: request.id));
|
||||
),
|
||||
id: request.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ class ScanData {
|
|||
final Map<String, String> labels;
|
||||
final List<int> labelIndexes;
|
||||
final bool isSingleScan;
|
||||
final bool shouldSwitchNodes;
|
||||
|
||||
ScanData({
|
||||
required this.silentPaymentsWallets,
|
||||
|
@ -19,6 +20,7 @@ class ScanData {
|
|||
required this.labels,
|
||||
required this.labelIndexes,
|
||||
required this.isSingleScan,
|
||||
required this.shouldSwitchNodes,
|
||||
});
|
||||
|
||||
factory ScanData.fromHeight(ScanData scanData, int newHeight) {
|
||||
|
@ -31,6 +33,7 @@ class ScanData {
|
|||
labels: scanData.labels,
|
||||
labelIndexes: scanData.labelIndexes,
|
||||
isSingleScan: scanData.isSingleScan,
|
||||
shouldSwitchNodes: scanData.shouldSwitchNodes,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -44,6 +47,7 @@ class ScanData {
|
|||
'labels': labels,
|
||||
'labelIndexes': labelIndexes,
|
||||
'isSingleScan': isSingleScan,
|
||||
'shouldSwitchNodes': shouldSwitchNodes,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -60,6 +64,7 @@ class ScanData {
|
|||
labels: json['labels'] as Map<String, String>,
|
||||
labelIndexes: (json['labelIndexes'] as List).map((e) => e as int).toList(),
|
||||
isSingleScan: json['isSingleScan'] as bool,
|
||||
shouldSwitchNodes: json['shouldSwitchNodes'] as bool,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -288,6 +288,11 @@ class CWBitcoin extends Bitcoin {
|
|||
return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType);
|
||||
}
|
||||
|
||||
@override
|
||||
bool isReceiveOptionSP(ReceivePageOption option) {
|
||||
return option.value == BitcoinReceivePageOption.silent_payments.value;
|
||||
}
|
||||
|
||||
@override
|
||||
bool hasSelectedSilentPayments(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
|
@ -610,7 +615,7 @@ class CWBitcoin extends Bitcoin {
|
|||
@override
|
||||
@computed
|
||||
bool getScanningActive(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
final bitcoinWallet = wallet as BitcoinWallet;
|
||||
return bitcoinWallet.silentPaymentsScanningActive;
|
||||
}
|
||||
|
||||
|
@ -620,6 +625,12 @@ class CWBitcoin extends Bitcoin {
|
|||
bitcoinWallet.setSilentPaymentsScanning(active);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> allowToSwitchNodesForScanning(Object wallet, bool allow) async {
|
||||
final bitcoinWallet = wallet as BitcoinWallet;
|
||||
bitcoinWallet.allowedToSwitchNodesForScanning = allow;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isTestnet(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
|
|
56
lib/di.dart
56
lib/di.dart
|
@ -472,10 +472,14 @@ Future<void> setup({
|
|||
getIt.get<SeedSettingsViewModel>(),
|
||||
type: type));
|
||||
|
||||
getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel(
|
||||
getIt.registerFactoryParam<WalletAddressListViewModel, ReceivePageOption?, void>(
|
||||
(ReceivePageOption? addressType, _) => WalletAddressListViewModel(
|
||||
appStore: getIt.get<AppStore>(),
|
||||
yatStore: getIt.get<YatStore>(),
|
||||
fiatConversionStore: getIt.get<FiatConversionStore>()));
|
||||
fiatConversionStore: getIt.get<FiatConversionStore>(),
|
||||
addressType: addressType,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactory(() => BalanceViewModel(
|
||||
appStore: getIt.get<AppStore>(),
|
||||
|
@ -704,12 +708,26 @@ Future<void> setup({
|
|||
getIt.get<ReceiveOptionViewModel>(param1: pageOption));
|
||||
});
|
||||
|
||||
getIt.registerFactory<ReceivePage>(
|
||||
() => ReceivePage(addressListViewModel: getIt.get<WalletAddressListViewModel>()));
|
||||
getIt.registerFactory<AddressPage>(() => AddressPage(
|
||||
addressListViewModel: getIt.get<WalletAddressListViewModel>(),
|
||||
getIt.registerFactoryParam<ReceivePage, ReceivePageOption?, void>(
|
||||
(ReceivePageOption? addressType, _) => ReceivePage(
|
||||
addressListViewModel: getIt.get<WalletAddressListViewModel>(
|
||||
param1: addressType,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<AddressPage, ReceivePageOption?, void>(
|
||||
(ReceivePageOption? addressType, _) => AddressPage(
|
||||
addressListViewModel: getIt.get<WalletAddressListViewModel>(
|
||||
param1: addressType,
|
||||
),
|
||||
dashboardViewModel: getIt.get<DashboardViewModel>(),
|
||||
receiveOptionViewModel: getIt.get<ReceiveOptionViewModel>()));
|
||||
receiveOptionViewModel: getIt.get<ReceiveOptionViewModel>(
|
||||
param1: addressType,
|
||||
),
|
||||
addressType: addressType,
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactoryParam<WalletAddressEditOrCreateViewModel, WalletAddressListItem?, void>(
|
||||
(WalletAddressListItem? item, _) =>
|
||||
|
@ -904,11 +922,11 @@ Future<void> setup({
|
|||
getIt.registerFactory(() => WalletKeysViewModel(getIt.get<AppStore>()));
|
||||
|
||||
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
|
||||
|
||||
|
||||
getIt.registerFactory(() => AnimatedURModel(getIt.get<AppStore>()));
|
||||
|
||||
getIt.registerFactoryParam<AnimatedURPage, String, void>((String urQr, _) =>
|
||||
AnimatedURPage(getIt.get<AnimatedURModel>(), urQr: urQr));
|
||||
getIt.registerFactoryParam<AnimatedURPage, String, void>(
|
||||
(String urQr, _) => AnimatedURPage(getIt.get<AnimatedURModel>(), urQr: urQr));
|
||||
|
||||
getIt.registerFactoryParam<ContactViewModel, ContactRecord?, void>(
|
||||
(ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact));
|
||||
|
@ -1004,8 +1022,8 @@ Future<void> setup({
|
|||
));
|
||||
|
||||
getIt.registerFactory<MeldBuyProvider>(() => MeldBuyProvider(
|
||||
wallet: getIt.get<AppStore>().wallet!,
|
||||
));
|
||||
wallet: getIt.get<AppStore>().wallet!,
|
||||
));
|
||||
|
||||
getIt.registerFactoryParam<WebViewPage, String, Uri>((title, uri) => WebViewPage(title, uri));
|
||||
|
||||
|
@ -1207,16 +1225,15 @@ Future<void> setup({
|
|||
final items = args.first as List<SelectableItem>;
|
||||
final pickAnOption = args[1] as void Function(SelectableOption option)?;
|
||||
final confirmOption = args[2] as void Function(BuildContext contex)?;
|
||||
return BuyOptionsPage(
|
||||
items: items, pickAnOption: pickAnOption, confirmOption: confirmOption);
|
||||
return BuyOptionsPage(items: items, pickAnOption: pickAnOption, confirmOption: confirmOption);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<PaymentMethodOptionsPage, List<dynamic>, void>((List<dynamic> args, _) {
|
||||
getIt
|
||||
.registerFactoryParam<PaymentMethodOptionsPage, List<dynamic>, void>((List<dynamic> args, _) {
|
||||
final items = args.first as List<SelectableOption>;
|
||||
final pickAnOption = args[1] as void Function(SelectableOption option)?;
|
||||
|
||||
return PaymentMethodOptionsPage(
|
||||
items: items, pickAnOption: pickAnOption);
|
||||
return PaymentMethodOptionsPage(items: items, pickAnOption: pickAnOption);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() {
|
||||
|
@ -1300,9 +1317,8 @@ Future<void> setup({
|
|||
getIt.registerFactory<CakePayService>(
|
||||
() => CakePayService(getIt.get<SecureStorage>(), getIt.get<CakePayApi>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => CakePayCardsListViewModel(cakePayService: getIt.get<CakePayService>(),
|
||||
settingsStore: getIt.get<SettingsStore>()));
|
||||
getIt.registerFactory(() => CakePayCardsListViewModel(
|
||||
cakePayService: getIt.get<CakePayService>(), settingsStore: getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactory(() => CakePayAuthViewModel(cakePayService: getIt.get<CakePayService>()));
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ import 'package:cake_wallet/wallet_types.g.dart';
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/nano_account.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
|
@ -376,11 +377,21 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
fullscreenDialog: true, builder: (_) => getIt.get<SendTemplatePage>());
|
||||
|
||||
case Routes.receive:
|
||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ReceivePage>());
|
||||
final args = settings.arguments as Map<String, dynamic>?;
|
||||
final addressType = args?['addressType'] as ReceivePageOption?;
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<ReceivePage>(param1: addressType),
|
||||
);
|
||||
|
||||
case Routes.addressPage:
|
||||
final args = settings.arguments as Map<String, dynamic>?;
|
||||
final addressType = args?['addressType'] as ReceivePageOption?;
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true, builder: (_) => getIt.get<AddressPage>());
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => getIt.get<AddressPage>(param1: addressType),
|
||||
);
|
||||
|
||||
case Routes.transactionDetails:
|
||||
return CupertinoPageRoute<void>(
|
||||
|
@ -588,7 +599,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
|
||||
case Routes.paymentMethodOptionsPage:
|
||||
final args = settings.arguments as List;
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<PaymentMethodOptionsPage>(param1: args));
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<PaymentMethodOptionsPage>(param1: args));
|
||||
|
||||
case Routes.buyWebView:
|
||||
final args = settings.arguments as List;
|
||||
|
@ -751,7 +763,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
return MaterialPageRoute<void>(builder: (_) => getIt.get<Setup2FAInfoPage>());
|
||||
|
||||
case Routes.urqrAnimatedPage:
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<AnimatedURPage>(param1: settings.arguments));
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<AnimatedURPage>(param1: settings.arguments));
|
||||
|
||||
case Routes.homeSettings:
|
||||
return CupertinoPageRoute<void>(
|
||||
|
|
|
@ -32,9 +32,11 @@ class AddressPage extends BasePage {
|
|||
required this.addressListViewModel,
|
||||
required this.dashboardViewModel,
|
||||
required this.receiveOptionViewModel,
|
||||
ReceivePageOption? addressType,
|
||||
}) : _cryptoAmountFocus = FocusNode(),
|
||||
_formKey = GlobalKey<FormState>(),
|
||||
_amountController = TextEditingController() {
|
||||
_amountController = TextEditingController(),
|
||||
_addressType = addressType {
|
||||
_amountController.addListener(() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
addressListViewModel.changeAmount(
|
||||
|
@ -49,6 +51,7 @@ class AddressPage extends BasePage {
|
|||
final ReceiveOptionViewModel receiveOptionViewModel;
|
||||
final TextEditingController _amountController;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
ReceivePageOption? _addressType;
|
||||
|
||||
final FocusNode _cryptoAmountFocus;
|
||||
|
||||
|
@ -190,7 +193,11 @@ class AddressPage extends BasePage {
|
|||
if (addressListViewModel.hasAddressList) {
|
||||
return SelectButton(
|
||||
text: addressListViewModel.buttonTitle,
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.receive),
|
||||
onTap: () => Navigator.pushNamed(
|
||||
context,
|
||||
Routes.receive,
|
||||
arguments: {'addressType': _addressType},
|
||||
),
|
||||
textColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||
borderColor: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
|
@ -354,12 +355,108 @@ class CryptoBalanceWidget extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => StandardSwitch(
|
||||
value: dashboardViewModel.silentPaymentsScanningActive,
|
||||
onTaped: () => _toggleSilentPaymentsScanning(context),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Semantics(
|
||||
label: S.of(context).receive,
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.addressPage,
|
||||
arguments: {
|
||||
'addressType': bitcoin!
|
||||
.getBitcoinReceivePageOptions()
|
||||
.where(
|
||||
(option) => option.value == "Silent Payments",
|
||||
)
|
||||
.first
|
||||
},
|
||||
);
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Colors.grey.shade400.withAlpha(50),
|
||||
side: BorderSide(
|
||||
color: Colors.grey.shade400.withAlpha(50), width: 0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
height: 30,
|
||||
width: 30,
|
||||
'assets/images/received.png',
|
||||
color: Theme.of(context)
|
||||
.extension<BalancePageTheme>()!
|
||||
.balanceAmountColor,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
S.of(context).receive,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<BalancePageTheme>()!
|
||||
.textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
SizedBox(width: 24),
|
||||
Expanded(
|
||||
child: Semantics(
|
||||
label: S.of(context).scan,
|
||||
child: OutlinedButton(
|
||||
onPressed: () => _toggleSilentPaymentsScanning(context),
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Colors.grey.shade400.withAlpha(50),
|
||||
side: BorderSide(
|
||||
color: Colors.grey.shade400.withAlpha(50), width: 0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardSwitch(
|
||||
value:
|
||||
dashboardViewModel.silentPaymentsScanningActive,
|
||||
onTaped: () =>
|
||||
_toggleSilentPaymentsScanning(context),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
S.of(context).scan,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<BalancePageTheme>()!
|
||||
.textColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -466,29 +563,40 @@ class CryptoBalanceWidget extends StatelessWidget {
|
|||
Future<void> _toggleSilentPaymentsScanning(BuildContext context) async {
|
||||
final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive;
|
||||
final newValue = !isSilentPaymentsScanningActive;
|
||||
|
||||
final willScan = newValue == true;
|
||||
dashboardViewModel.silentPaymentsScanningActive = newValue;
|
||||
|
||||
final needsToSwitch = !isSilentPaymentsScanningActive &&
|
||||
await bitcoin!.getNodeIsElectrsSPEnabled(dashboardViewModel.wallet) == false;
|
||||
if (willScan) {
|
||||
late bool isElectrsSPEnabled;
|
||||
try {
|
||||
isElectrsSPEnabled = await bitcoin!
|
||||
.getNodeIsElectrsSPEnabled(dashboardViewModel.wallet)
|
||||
.timeout(const Duration(seconds: 3));
|
||||
} on TimeoutException {
|
||||
isElectrsSPEnabled = false;
|
||||
}
|
||||
|
||||
if (needsToSwitch) {
|
||||
return showPopUp<void>(
|
||||
final needsToSwitch = isElectrsSPEnabled == false;
|
||||
if (needsToSwitch) {
|
||||
return showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertWithTwoActions(
|
||||
alertTitle: S.of(context).change_current_node_title,
|
||||
alertContent: S.of(context).confirm_silent_payments_switch_node,
|
||||
rightButtonText: S.of(context).confirm,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () {
|
||||
dashboardViewModel.setSilentPaymentsScanning(newValue);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
actionLeftButton: () {
|
||||
dashboardViewModel.silentPaymentsScanningActive = isSilentPaymentsScanningActive;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
));
|
||||
alertTitle: S.of(context).change_current_node_title,
|
||||
alertContent: S.of(context).confirm_silent_payments_switch_node,
|
||||
rightButtonText: S.of(context).confirm,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () {
|
||||
dashboardViewModel.allowSilentPaymentsScanning(true);
|
||||
dashboardViewModel.setSilentPaymentsScanning(true);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
actionLeftButton: () {
|
||||
dashboardViewModel.silentPaymentsScanningActive = isSilentPaymentsScanningActive;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return dashboardViewModel.setSilentPaymentsScanning(newValue);
|
||||
|
@ -1045,10 +1153,9 @@ class BalanceRowWidget extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Colors.grey.shade400
|
||||
.withAlpha(50),
|
||||
side: BorderSide(color: Colors.grey.shade400
|
||||
.withAlpha(50), width: 0),
|
||||
backgroundColor: Colors.grey.shade400.withAlpha(50),
|
||||
side:
|
||||
BorderSide(color: Colors.grey.shade400.withAlpha(50), width: 0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
|
@ -1104,10 +1211,9 @@ class BalanceRowWidget extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Colors.grey.shade400
|
||||
.withAlpha(50),
|
||||
side: BorderSide(color: Colors.grey.shade400
|
||||
.withAlpha(50), width: 0),
|
||||
backgroundColor: Colors.grey.shade400.withAlpha(50),
|
||||
side:
|
||||
BorderSide(color: Colors.grey.shade400.withAlpha(50), width: 0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -35,7 +36,8 @@ class RescanPage extends BasePage {
|
|||
isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan,
|
||||
isMwebScan: _rescanViewModel.isMwebScan,
|
||||
doSingleScan: _rescanViewModel.doSingleScan,
|
||||
hasDatePicker: !_rescanViewModel.isMwebScan,// disable date picker for mweb for now
|
||||
hasDatePicker:
|
||||
!_rescanViewModel.isMwebScan, // disable date picker for mweb for now
|
||||
toggleSingleScan: () =>
|
||||
_rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan,
|
||||
walletType: _rescanViewModel.wallet.type,
|
||||
|
@ -69,24 +71,32 @@ class RescanPage extends BasePage {
|
|||
|
||||
Navigator.of(context).pop();
|
||||
|
||||
final needsToSwitch =
|
||||
await bitcoin!.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) == false;
|
||||
late bool isElectrsSPEnabled;
|
||||
try {
|
||||
isElectrsSPEnabled = await bitcoin!
|
||||
.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet)
|
||||
.timeout(const Duration(seconds: 3));
|
||||
} on TimeoutException {
|
||||
isElectrsSPEnabled = false;
|
||||
}
|
||||
|
||||
final needsToSwitch = isElectrsSPEnabled == false;
|
||||
if (needsToSwitch) {
|
||||
return showPopUp<void>(
|
||||
context: navigatorKey.currentState!.context,
|
||||
builder: (BuildContext _dialogContext) => AlertWithTwoActions(
|
||||
alertTitle: S.of(_dialogContext).change_current_node_title,
|
||||
alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node,
|
||||
rightButtonText: S.of(_dialogContext).confirm,
|
||||
leftButtonText: S.of(_dialogContext).cancel,
|
||||
actionRightButton: () async {
|
||||
Navigator.of(_dialogContext).pop();
|
||||
context: context,
|
||||
builder: (BuildContext _dialogContext) => AlertWithTwoActions(
|
||||
alertTitle: S.of(_dialogContext).change_current_node_title,
|
||||
alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node,
|
||||
rightButtonText: S.of(_dialogContext).confirm,
|
||||
leftButtonText: S.of(_dialogContext).cancel,
|
||||
actionRightButton: () async {
|
||||
Navigator.of(_dialogContext).pop();
|
||||
|
||||
_rescanViewModel.rescanCurrentWallet(restoreHeight: height);
|
||||
},
|
||||
actionLeftButton: () => Navigator.of(_dialogContext).pop(),
|
||||
));
|
||||
_rescanViewModel.rescanCurrentWallet(restoreHeight: height);
|
||||
},
|
||||
actionLeftButton: () => Navigator.of(_dialogContext).pop(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_rescanViewModel.rescanCurrentWallet(restoreHeight: height);
|
||||
|
|
|
@ -428,7 +428,8 @@ abstract class DashboardViewModelBase with Store {
|
|||
// to not cause work duplication, this will do the job as well, it will be slightly less precise
|
||||
// about what happened - but still enough.
|
||||
// if (keys['privateSpendKey'] == List.generate(64, (index) => "0").join("")) "Private spend key is 0",
|
||||
if (keys['privateViewKey'] == List.generate(64, (index) => "0").join("") && !wallet.isHardwareWallet)
|
||||
if (keys['privateViewKey'] == List.generate(64, (index) => "0").join("") &&
|
||||
!wallet.isHardwareWallet)
|
||||
"private view key is 0",
|
||||
// if (keys['publicSpendKey'] == List.generate(64, (index) => "0").join("")) "public spend key is 0",
|
||||
if (keys['publicViewKey'] == List.generate(64, (index) => "0").join(""))
|
||||
|
@ -454,6 +455,13 @@ abstract class DashboardViewModelBase with Store {
|
|||
@observable
|
||||
bool silentPaymentsScanningActive = false;
|
||||
|
||||
@action
|
||||
void allowSilentPaymentsScanning(bool allow) {
|
||||
if (hasSilentPayments) {
|
||||
bitcoin!.allowToSwitchNodesForScanning(wallet, allow);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void setSilentPaymentsScanning(bool active) {
|
||||
silentPaymentsScanningActive = active;
|
||||
|
|
|
@ -25,6 +25,7 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
|
|||
import 'package:cake_wallet/wownero/wownero.dart';
|
||||
import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -209,6 +210,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
required AppStore appStore,
|
||||
required this.yatStore,
|
||||
required this.fiatConversionStore,
|
||||
ReceivePageOption? addressType,
|
||||
}) : _baseItems = <ListItem>[],
|
||||
selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type),
|
||||
_cryptoNumberFormat = NumberFormat(_cryptoNumberPattern),
|
||||
|
@ -216,6 +218,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
.contains(appStore.wallet!.type),
|
||||
amount = '',
|
||||
_settingsStore = appStore.settingsStore,
|
||||
_addressType = addressType,
|
||||
super(appStore: appStore) {
|
||||
_init();
|
||||
}
|
||||
|
@ -234,6 +237,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
|
||||
final FiatConversionStore fiatConversionStore;
|
||||
final SettingsStore _settingsStore;
|
||||
final ReceivePageOption? _addressType;
|
||||
|
||||
double? _fiatRate;
|
||||
String _rawAmount = '';
|
||||
|
@ -264,8 +268,19 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
WalletType get type => wallet.type;
|
||||
|
||||
@computed
|
||||
WalletAddressListItem get address =>
|
||||
WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false);
|
||||
WalletAddressListItem get address {
|
||||
if (_addressType != null) {
|
||||
final shouldForceSP = _addressType != null && bitcoin!.isReceiveOptionSP(_addressType!);
|
||||
if (shouldForceSP) {
|
||||
return WalletAddressListItem(
|
||||
address: bitcoin!.getSilentPaymentAddresses(wallet).first.address,
|
||||
isPrimary: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false);
|
||||
}
|
||||
|
||||
@computed
|
||||
PaymentURI get uri {
|
||||
|
@ -354,7 +369,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
}
|
||||
|
||||
if (isElectrumWallet) {
|
||||
if (bitcoin!.hasSelectedSilentPayments(wallet)) {
|
||||
final hasSelectedSP = bitcoin!.hasSelectedSilentPayments(wallet);
|
||||
final shouldForceSP = _addressType != null && bitcoin!.isReceiveOptionSP(_addressType!);
|
||||
|
||||
if (hasSelectedSP || shouldForceSP) {
|
||||
final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) {
|
||||
final isPrimary = address.id == 0;
|
||||
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "حفظ كلمة المرور الاحتياطية",
|
||||
"save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ",
|
||||
"saved_the_trade_id": "لقد تم حفظ معرف العملية",
|
||||
"scan": "مسح",
|
||||
"scan_one_block": "مسح كتلة واحدة",
|
||||
"scan_qr_code": "امسح رمز QR ضوئيًا",
|
||||
"scan_qr_code_to_get_address": "امسح ال QR للحصول على العنوان",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Запазване на паролата за възстановяване",
|
||||
"save_to_downloads": "Запазване в Изтегляния",
|
||||
"saved_the_trade_id": "Запазих trade ID-то",
|
||||
"scan": "Сканиране",
|
||||
"scan_one_block": "Сканирайте един блок",
|
||||
"scan_qr_code": "Сканирайте QR кода, за да получите адреса",
|
||||
"scan_qr_code_to_get_address": "Сканирайте QR кода, за да получите адреса",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Uložit heslo pro zálohy",
|
||||
"save_to_downloads": "Uložit do Stažených souborů",
|
||||
"saved_the_trade_id": "Uložil jsem si ID transakce (trade ID)",
|
||||
"scan": "Skenovat",
|
||||
"scan_one_block": "Prohledejte jeden blok",
|
||||
"scan_qr_code": "Naskenujte QR kód pro získání adresy",
|
||||
"scan_qr_code_to_get_address": "Prohledejte QR kód a získejte adresu",
|
||||
|
|
|
@ -595,6 +595,7 @@
|
|||
"save_backup_password_alert": "Sicherungskennwort speichern",
|
||||
"save_to_downloads": "Unter „Downloads“ speichern",
|
||||
"saved_the_trade_id": "Ich habe die Handels-ID gespeichert",
|
||||
"scan": "Scan",
|
||||
"scan_one_block": "Einen Block scannen",
|
||||
"scan_qr_code": "QR-Code scannen",
|
||||
"scan_qr_code_to_get_address": "Scannen Sie den QR-Code, um die Adresse zu erhalten",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Save backup password",
|
||||
"save_to_downloads": "Save to Downloads",
|
||||
"saved_the_trade_id": "I've saved the trade ID",
|
||||
"scan": "Scan",
|
||||
"scan_one_block": "Scan one block",
|
||||
"scan_qr_code": "Scan QR code",
|
||||
"scan_qr_code_to_get_address": "Scan the QR code to get the address",
|
||||
|
|
|
@ -595,6 +595,7 @@
|
|||
"save_backup_password_alert": "Guardar contraseña de respaldo",
|
||||
"save_to_downloads": "Guardar en Descargas",
|
||||
"saved_the_trade_id": "He salvado comercial ID",
|
||||
"scan": "Escanear",
|
||||
"scan_one_block": "Escanear un bloque",
|
||||
"scan_qr_code": "Escanear código QR",
|
||||
"scan_qr_code_to_get_address": "Escanea el código QR para obtener la dirección",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Enregistrer le mot de passe de sauvegarde",
|
||||
"save_to_downloads": "Enregistrer dans les téléchargements",
|
||||
"saved_the_trade_id": "J'ai sauvegardé l'ID d'échange",
|
||||
"scan": "Balayage",
|
||||
"scan_one_block": "Scanner un bloc",
|
||||
"scan_qr_code": "Scannez le QR code",
|
||||
"scan_qr_code_to_get_address": "Scannez le QR code pour obtenir l'adresse",
|
||||
|
|
|
@ -596,6 +596,7 @@
|
|||
"save_backup_password_alert": "Ajiye kalmar sirri ta ajiya",
|
||||
"save_to_downloads": "Ajiye zuwa Zazzagewa",
|
||||
"saved_the_trade_id": "Na ajiye ID na ciniki",
|
||||
"scan": "Scan",
|
||||
"scan_one_block": "Duba toshe daya",
|
||||
"scan_qr_code": "Gani QR kodin",
|
||||
"scan_qr_code_to_get_address": "Duba lambar QR don samun adireshin",
|
||||
|
|
|
@ -596,6 +596,7 @@
|
|||
"save_backup_password_alert": "बैकअप पासवर्ड सेव करें",
|
||||
"save_to_downloads": "डाउनलोड में सहेजें",
|
||||
"saved_the_trade_id": "मैंने व्यापार बचा लिया है ID",
|
||||
"scan": "स्कैन",
|
||||
"scan_one_block": "एक ब्लॉक को स्कैन करना",
|
||||
"scan_qr_code": "स्कैन क्यू आर कोड",
|
||||
"scan_qr_code_to_get_address": "पता प्राप्त करने के लिए QR कोड स्कैन करें",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Spremi lozinku za sigurnosnu kopiju",
|
||||
"save_to_downloads": "Spremi u Preuzimanja",
|
||||
"saved_the_trade_id": "Spremio/la sam transakcijski ID",
|
||||
"scan": "Skenirati",
|
||||
"scan_one_block": "Skenirajte jedan blok",
|
||||
"scan_qr_code": "Skenirajte QR kod",
|
||||
"scan_qr_code_to_get_address": "Skeniraj QR kod za dobivanje adrese",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Պահպանել կրկնօրինակի գաղտնաբառը",
|
||||
"save_to_downloads": "Պահպանել ներբեռնումներում",
|
||||
"saved_the_trade_id": "Ես պահպանել եմ առևտրի ID-ն",
|
||||
"scan": "Սկանավորել",
|
||||
"scan_one_block": "Սկանավորել մեկ բլոկ",
|
||||
"scan_qr_code": "Սկանավորել QR կոդ",
|
||||
"scan_qr_code_to_get_address": "Սկանավորել QR կոդը հասցեն ստանալու համար",
|
||||
|
|
|
@ -597,6 +597,7 @@
|
|||
"save_backup_password_alert": "Simpan kata sandi cadangan",
|
||||
"save_to_downloads": "Simpan ke Unduhan",
|
||||
"saved_the_trade_id": "Saya telah menyimpan ID perdagangan",
|
||||
"scan": "Pindai",
|
||||
"scan_one_block": "Pindai satu blok",
|
||||
"scan_qr_code": "Scan kode QR untuk mendapatkan alamat",
|
||||
"scan_qr_code_to_get_address": "Pindai kode QR untuk mendapatkan alamat",
|
||||
|
|
|
@ -596,6 +596,7 @@
|
|||
"save_backup_password_alert": "Salva password Backup",
|
||||
"save_to_downloads": "Salva in Download",
|
||||
"saved_the_trade_id": "Ho salvato l'ID dello scambio",
|
||||
"scan": "Scansione",
|
||||
"scan_one_block": "Scansionare un blocco",
|
||||
"scan_qr_code": "Scansiona il codice QR",
|
||||
"scan_qr_code_to_get_address": "Scansiona il codice QR per ottenere l'indirizzo",
|
||||
|
|
|
@ -595,6 +595,7 @@
|
|||
"save_backup_password_alert": "バックアップパスワードを保存する",
|
||||
"save_to_downloads": "ダウンロードに保存",
|
||||
"saved_the_trade_id": "取引IDを保存しました",
|
||||
"scan": "スキャン",
|
||||
"scan_one_block": "1つのブロックをスキャンします",
|
||||
"scan_qr_code": "QRコードをスキャン",
|
||||
"scan_qr_code_to_get_address": "QRコードをスキャンして住所を取得します",
|
||||
|
|
|
@ -595,6 +595,7 @@
|
|||
"save_backup_password_alert": "백업 비밀번호 저장",
|
||||
"save_to_downloads": "다운로드에 저장",
|
||||
"saved_the_trade_id": "거래 ID를 저장했습니다",
|
||||
"scan": "주사",
|
||||
"scan_one_block": "하나의 블록을 스캔하십시오",
|
||||
"scan_qr_code": "QR 코드 스캔",
|
||||
"scan_qr_code_to_get_address": "QR 코드를 스캔하여 주소를 얻습니다.",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "အရန်စကားဝှက်ကို သိမ်းဆည်းပါ။",
|
||||
"save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။",
|
||||
"saved_the_trade_id": "ကုန်သွယ်မှု ID ကို သိမ်းဆည်းပြီးပါပြီ။",
|
||||
"scan": "စကင်ဖတ်",
|
||||
"scan_one_block": "တစ်ကွက်ကိုစကင်ဖတ်စစ်ဆေးပါ",
|
||||
"scan_qr_code": "QR ကုဒ်ကို စကင်န်ဖတ်ပါ။",
|
||||
"scan_qr_code_to_get_address": "လိပ်စာရယူရန် QR ကုဒ်ကို စကင်န်ဖတ်ပါ။",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Bewaar back-upwachtwoord",
|
||||
"save_to_downloads": "Opslaan in downloads",
|
||||
"saved_the_trade_id": "Ik heb de ruil-ID opgeslagen",
|
||||
"scan": "Scannen",
|
||||
"scan_one_block": "Scan een blok",
|
||||
"scan_qr_code": "Scan QR-code",
|
||||
"scan_qr_code_to_get_address": "Scan de QR-code om het adres te krijgen",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Zapisz hasło kopii zapasowej",
|
||||
"save_to_downloads": "Zapisz w Pobranych",
|
||||
"saved_the_trade_id": "Zapisałem ID",
|
||||
"scan": "Skandować",
|
||||
"scan_one_block": "Zeskanuj jeden blok",
|
||||
"scan_qr_code": "Skanowania QR code",
|
||||
"scan_qr_code_to_get_address": "Zeskanuj kod QR, aby uzyskać adres",
|
||||
|
|
|
@ -596,6 +596,7 @@
|
|||
"save_backup_password_alert": "Salvar senha de backup",
|
||||
"save_to_downloads": "Salvar em Downloads",
|
||||
"saved_the_trade_id": "ID da troca salvo",
|
||||
"scan": "Scan",
|
||||
"scan_one_block": "Escanear um bloco",
|
||||
"scan_qr_code": "Escanear código QR",
|
||||
"scan_qr_code_to_get_address": "Digitalize o código QR para obter o endereço",
|
||||
|
@ -956,4 +957,4 @@
|
|||
"you_will_get": "Converter para",
|
||||
"you_will_send": "Converter de",
|
||||
"yy": "aa"
|
||||
}
|
||||
}
|
|
@ -595,6 +595,7 @@
|
|||
"save_backup_password_alert": "Сохранить пароль резервной копии",
|
||||
"save_to_downloads": "Сохранить в загрузках",
|
||||
"saved_the_trade_id": "Я сохранил ID сделки",
|
||||
"scan": "Сканирование",
|
||||
"scan_one_block": "Сканируйте один блок",
|
||||
"scan_qr_code": "Сканировать QR-код",
|
||||
"scan_qr_code_to_get_address": "Отсканируйте QR-код для получения адреса",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "บันทึกรหัสผ่านสำรอง",
|
||||
"save_to_downloads": "บันทึกลงดาวน์โหลด",
|
||||
"saved_the_trade_id": "ฉันได้บันทึก ID ของการซื้อขายแล้ว",
|
||||
"scan": "สแกน",
|
||||
"scan_one_block": "สแกนหนึ่งบล็อก",
|
||||
"scan_qr_code": "สแกนรหัส QR",
|
||||
"scan_qr_code_to_get_address": "สแกน QR code เพื่อรับที่อยู่",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "I-save ang backup na password",
|
||||
"save_to_downloads": "I-save sa mga Pag-download",
|
||||
"saved_the_trade_id": "Nai-save ko na ang trade ID",
|
||||
"scan": "I -scan",
|
||||
"scan_one_block": "I-scan ang isang bloke",
|
||||
"scan_qr_code": "I-scan ang QR code",
|
||||
"scan_qr_code_to_get_address": "I-scan ang QR code upang makuha ang address",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "Yedek parolasını kaydet",
|
||||
"save_to_downloads": "İndirilenlere Kaydet",
|
||||
"saved_the_trade_id": "Takas ID'imi kaydettim",
|
||||
"scan": "Taramak",
|
||||
"scan_one_block": "Bir bloğu tara",
|
||||
"scan_qr_code": "QR kodunu tarayın",
|
||||
"scan_qr_code_to_get_address": "Adresi getirmek için QR kodunu tara",
|
||||
|
|
|
@ -595,6 +595,7 @@
|
|||
"save_backup_password_alert": "Зберегти пароль резервної копії",
|
||||
"save_to_downloads": "Зберегти до завантажень",
|
||||
"saved_the_trade_id": "Я зберіг ID операції",
|
||||
"scan": "Сканувати",
|
||||
"scan_one_block": "Сканувати один блок",
|
||||
"scan_qr_code": "Відскануйте QR-код",
|
||||
"scan_qr_code_to_get_address": "Скануйте QR-код для одержання адреси",
|
||||
|
|
|
@ -596,6 +596,7 @@
|
|||
"save_backup_password_alert": "بیک اپ پاس ورڈ محفوظ کریں۔",
|
||||
"save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ",
|
||||
"saved_the_trade_id": "میں نے تجارتی ID محفوظ کر لی ہے۔",
|
||||
"scan": "اسکین",
|
||||
"scan_one_block": "ایک بلاک اسکین کریں",
|
||||
"scan_qr_code": "پتہ حاصل کرنے کے لیے QR کوڈ اسکین کریں۔",
|
||||
"scan_qr_code_to_get_address": "پتہ حاصل کرنے کے لئے QR کوڈ کو اسکین کریں",
|
||||
|
|
|
@ -593,6 +593,7 @@
|
|||
"save_backup_password_alert": "Lưu mật khẩu sao lưu",
|
||||
"save_to_downloads": "Lưu vào Tải xuống",
|
||||
"saved_the_trade_id": "Tôi đã lưu ID giao dịch",
|
||||
"scan": "Quét",
|
||||
"scan_one_block": "Quét một khối",
|
||||
"scan_qr_code": "Quét mã QR",
|
||||
"scan_qr_code_to_get_address": "Quét mã QR để nhận địa chỉ",
|
||||
|
|
|
@ -595,6 +595,7 @@
|
|||
"save_backup_password_alert": "Pamọ́ ọ̀rọ̀ aṣínà ti ẹ̀dà",
|
||||
"save_to_downloads": "Fipamọ si Awọn igbasilẹ",
|
||||
"saved_the_trade_id": "Mo ti pamọ́ àmì ìdánimọ̀ pàṣípààrọ̀",
|
||||
"scan": "Ọlọjẹ",
|
||||
"scan_one_block": "Ọlọjẹ ọkan bulọki",
|
||||
"scan_qr_code": "Yan QR koodu",
|
||||
"scan_qr_code_to_get_address": "Ṣayẹwo koodu QR naa lati gba adirẹsi naa",
|
||||
|
|
|
@ -594,6 +594,7 @@
|
|||
"save_backup_password_alert": "保存备份密码",
|
||||
"save_to_downloads": "保存到下载",
|
||||
"saved_the_trade_id": "我已经保存了交易编号",
|
||||
"scan": "扫描",
|
||||
"scan_one_block": "扫描一个街区",
|
||||
"scan_qr_code": "扫描二维码",
|
||||
"scan_qr_code_to_get_address": "扫描二维码获取地址",
|
||||
|
|
|
@ -227,11 +227,13 @@ abstract class Bitcoin {
|
|||
List<ReceivePageOption> getBitcoinReceivePageOptions();
|
||||
List<ReceivePageOption> getLitecoinReceivePageOptions();
|
||||
BitcoinAddressType getBitcoinAddressType(ReceivePageOption option);
|
||||
bool isReceiveOptionSP(ReceivePageOption option);
|
||||
bool hasSelectedSilentPayments(Object wallet);
|
||||
bool isBitcoinReceivePageOption(ReceivePageOption option);
|
||||
BitcoinAddressType getOptionToType(ReceivePageOption option);
|
||||
bool hasTaprootInput(PendingTransaction pendingTransaction);
|
||||
bool getScanningActive(Object wallet);
|
||||
Future<void> allowToSwitchNodesForScanning(Object wallet, bool allow);
|
||||
Future<void> setScanningActive(Object wallet, bool active);
|
||||
bool isTestnet(Object wallet);
|
||||
|
||||
|
|
Loading…
Reference in a new issue