mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-22 10:45:08 +00:00
feat: new electrs API & changes, fixes for last block scanning
This commit is contained in:
parent
4308e3e123
commit
dd532b4fe0
35 changed files with 205 additions and 148 deletions
|
@ -126,7 +126,8 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
super.isUsed = false,
|
||||
required this.silentPaymentTweak,
|
||||
required super.network,
|
||||
}) : super(type: SilentPaymentsAddresType.p2sp);
|
||||
required super.type,
|
||||
}) : super();
|
||||
|
||||
factory BitcoinSilentPaymentAddressRecord.fromJSON(String jsonSource,
|
||||
{BasedUtxoNetwork? network}) {
|
||||
|
@ -144,6 +145,10 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
? network
|
||||
: BasedUtxoNetwork.fromName(decoded['network'] as String),
|
||||
silentPaymentTweak: decoded['silent_payment_tweak'] as String?,
|
||||
type: decoded['type'] != null && decoded['type'] != ''
|
||||
? BitcoinAddressType.values
|
||||
.firstWhere((type) => type.toString() == decoded['type'] as String)
|
||||
: SilentPaymentsAddresType.p2sp,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -279,8 +279,11 @@ class ElectrumClient {
|
|||
Future<Map<String, dynamic>> getHeader({required int height}) async =>
|
||||
await call(method: 'blockchain.block.get_header', params: [height]) as Map<String, dynamic>;
|
||||
|
||||
Future<List<dynamic>> getTweaks({required int height}) async =>
|
||||
await callWithTimeout(method: 'blockchain.block.tweaks', params: [height]) as List<dynamic>;
|
||||
Future<Map<String, dynamic>> getTweaks({required int height, required int count}) async =>
|
||||
await callWithTimeout(
|
||||
method: 'blockchain.block.tweaks',
|
||||
params: [height, count],
|
||||
timeout: 10000) as Map<String, dynamic>;
|
||||
|
||||
Future<double> estimatefee({required int p}) =>
|
||||
call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) {
|
||||
|
|
|
@ -3,8 +3,11 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
|||
import 'package:cw_core/balance.dart';
|
||||
|
||||
class ElectrumBalance extends Balance {
|
||||
const ElectrumBalance({required this.confirmed, required this.unconfirmed, required this.frozen})
|
||||
: super(confirmed, unconfirmed);
|
||||
ElectrumBalance({
|
||||
required this.confirmed,
|
||||
required this.unconfirmed,
|
||||
required this.frozen,
|
||||
}) : super(confirmed, unconfirmed);
|
||||
|
||||
static ElectrumBalance? fromJSON(String? jsonSource) {
|
||||
if (jsonSource == null) {
|
||||
|
@ -19,7 +22,7 @@ class ElectrumBalance extends Balance {
|
|||
frozen: decoded['frozen'] as int? ?? 0);
|
||||
}
|
||||
|
||||
final int confirmed;
|
||||
int confirmed;
|
||||
final int unconfirmed;
|
||||
final int frozen;
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ import 'package:http/http.dart' as http;
|
|||
|
||||
part 'electrum_wallet.g.dart';
|
||||
|
||||
const SCANNING_BLOCK_COUNT = 50;
|
||||
|
||||
class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet;
|
||||
|
||||
abstract class ElectrumWalletBase
|
||||
|
@ -72,8 +74,12 @@ abstract class ElectrumWalletBase
|
|||
_scripthashesUpdateSubject = {},
|
||||
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
|
||||
? {
|
||||
currency:
|
||||
initialBalance ?? const ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0)
|
||||
currency: initialBalance ??
|
||||
ElectrumBalance(
|
||||
confirmed: 0,
|
||||
unconfirmed: 0,
|
||||
frozen: 0,
|
||||
)
|
||||
}
|
||||
: {}),
|
||||
this.unspentCoinsInfo = unspentCoinsInfo,
|
||||
|
@ -194,9 +200,9 @@ abstract class ElectrumWalletBase
|
|||
));
|
||||
|
||||
await for (var message in receivePort) {
|
||||
if (message is bool) {
|
||||
if (message is bool && message == false) {
|
||||
nodeSupportsSilentPayments = message;
|
||||
syncStatus = UnsupportedSyncStatus();
|
||||
syncStatus = TimedOutSyncStatus();
|
||||
}
|
||||
|
||||
if (message is Map<String, ElectrumTransactionInfo>) {
|
||||
|
@ -238,7 +244,7 @@ abstract class ElectrumWalletBase
|
|||
|
||||
await transactionHistory.save();
|
||||
await updateUnspent();
|
||||
await save();
|
||||
await updateBalance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +326,6 @@ abstract class ElectrumWalletBase
|
|||
{int? inputsCount,
|
||||
bool? hasSilentPayment}) async {
|
||||
final utxos = <UtxoWithAddress>[];
|
||||
List<ECPrivate> privateKeys = [];
|
||||
|
||||
var leftAmount = credentialsAmount;
|
||||
var allInputsAmount = 0;
|
||||
|
@ -340,13 +345,15 @@ abstract class ElectrumWalletBase
|
|||
final address = _addressTypeFromStr(utx.address, network);
|
||||
|
||||
ECPrivate? privkey;
|
||||
bool? isSilentPayment = false;
|
||||
if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
||||
privkey = walletAddresses.silentAddress!.b_spend.clone().tweakAdd(
|
||||
BigintUtils.fromBytes(BytesUtils.fromHexString(
|
||||
(utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord)
|
||||
.silentPaymentTweak!)),
|
||||
);
|
||||
privkey = walletAddresses.silentAddress!.b_spend.tweakAdd(
|
||||
BigintUtils.fromBytes(BytesUtils.fromHexString(
|
||||
(utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord)
|
||||
.silentPaymentTweak!)),
|
||||
);
|
||||
spendsSilentPayment = true;
|
||||
isSilentPayment = true;
|
||||
} else {
|
||||
privkey = generateECPrivate(
|
||||
hd: utx.bitcoinAddressRecord.isHidden
|
||||
|
@ -356,7 +363,6 @@ abstract class ElectrumWalletBase
|
|||
network: network);
|
||||
}
|
||||
|
||||
privateKeys.add(privkey);
|
||||
inputPrivKeyInfos.add(ECPrivateInfo(privkey, address.type == SegwitAddresType.p2tr));
|
||||
inputPubKeys.add(privkey.getPublic());
|
||||
vinOutpoints.add(Outpoint(txid: utx.hash, index: utx.vout));
|
||||
|
@ -368,6 +374,7 @@ abstract class ElectrumWalletBase
|
|||
value: BigInt.from(utx.value),
|
||||
vout: utx.vout,
|
||||
scriptType: _getScriptType(address),
|
||||
isSilentPayment: isSilentPayment,
|
||||
),
|
||||
ownerDetails:
|
||||
UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address),
|
||||
|
@ -485,7 +492,7 @@ abstract class ElectrumWalletBase
|
|||
|
||||
return EstimatedTxResult(
|
||||
utxos: utxos,
|
||||
privateKeys: privateKeys,
|
||||
inputPrivKeyInfos: inputPrivKeyInfos,
|
||||
fee: fee,
|
||||
amount: amount,
|
||||
spendsSilentPayment: spendsSilentPayment);
|
||||
|
@ -535,8 +542,13 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
final estimatedTx = await _estimateTxFeeAndInputsToUse(
|
||||
credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials,
|
||||
hasSilentPayment: hasSilentPayment);
|
||||
credentialsAmount,
|
||||
sendAll,
|
||||
outputAddresses,
|
||||
outputs,
|
||||
transactionCredentials,
|
||||
hasSilentPayment: hasSilentPayment,
|
||||
);
|
||||
|
||||
final txb = BitcoinTransactionBuilder(
|
||||
utxos: estimatedTx.utxos,
|
||||
|
@ -545,17 +557,21 @@ abstract class ElectrumWalletBase
|
|||
network: network);
|
||||
|
||||
final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) {
|
||||
final key = estimatedTx.privateKeys
|
||||
.firstWhereOrNull((element) => element.getPublic().toHex() == publicKey);
|
||||
final key = estimatedTx.inputPrivKeyInfos
|
||||
.firstWhereOrNull((element) => element.privkey.getPublic().toHex() == publicKey);
|
||||
|
||||
if (key == null) {
|
||||
throw Exception("Cannot find private key");
|
||||
}
|
||||
|
||||
if (utxo.utxo.isP2tr()) {
|
||||
return key.signTapRoot(txDigest, sighash: sighash);
|
||||
return key.privkey.signTapRoot(
|
||||
txDigest,
|
||||
sighash: sighash,
|
||||
tweak: utxo.utxo.isSilentPayment != true,
|
||||
);
|
||||
} else {
|
||||
return key.signInput(txDigest, sigHash: sighash);
|
||||
return key.privkey.signInput(txDigest, sigHash: sighash);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -820,7 +836,9 @@ abstract class ElectrumWalletBase
|
|||
(await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$hash/status"))).body);
|
||||
|
||||
time = status["block_time"] as int?;
|
||||
confirmations = currentChainTip! - (status["block_height"] as int? ?? 0);
|
||||
final height = status["block_height"] as int? ?? 0;
|
||||
confirmations =
|
||||
height > 0 ? (await electrumClient.getCurrentBlockChainTip())! - height + 1 : 0;
|
||||
} else {
|
||||
final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
|
||||
|
||||
|
@ -1056,6 +1074,19 @@ abstract class ElectrumWalletBase
|
|||
|
||||
Future<void> updateBalance() async {
|
||||
balance[currency] = await _fetchBalances();
|
||||
|
||||
// Update balance stored from scanned silent payment transactions
|
||||
try {
|
||||
transactionHistory.transactions.values.forEach((tx) {
|
||||
if (tx.unspents != null) {
|
||||
balance[currency]!.confirmed += tx.unspents!
|
||||
.where((unspent) => unspent.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord)
|
||||
.map((e) => e.value)
|
||||
.reduce((value, element) => value + element);
|
||||
}
|
||||
});
|
||||
} catch (_) {}
|
||||
|
||||
await save();
|
||||
}
|
||||
|
||||
|
@ -1194,9 +1225,9 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
try {
|
||||
final electrumClient = await getElectrumConnection();
|
||||
|
||||
List<dynamic>? tweaks;
|
||||
Map<String, dynamic>? tweaks;
|
||||
try {
|
||||
tweaks = await electrumClient.getTweaks(height: syncHeight);
|
||||
tweaks = await electrumClient.getTweaks(height: syncHeight, count: SCANNING_BLOCK_COUNT);
|
||||
} catch (e) {
|
||||
if (e is RequestFailedTimeoutException) {
|
||||
return scanData.sendPort.send(false);
|
||||
|
@ -1208,133 +1239,103 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight)));
|
||||
}
|
||||
|
||||
for (var i = 0; i < tweaks!.length; i++) {
|
||||
final blockHeights = tweaks!.keys;
|
||||
for (var i = 0; i < blockHeights.length; i++) {
|
||||
try {
|
||||
final details = tweaks[i] as Map<String, dynamic>;
|
||||
final outputPubkeys = (details["output_pubkeys"] as List<dynamic>);
|
||||
final tweak = details["tweak"].toString();
|
||||
final blockHeight = blockHeights.elementAt(i).toString();
|
||||
final blockTweaks = tweaks[blockHeight] as Map<String, dynamic>;
|
||||
|
||||
// TODO: if tx already scanned & stored skip
|
||||
// if (scanData.transactionHistoryIds.contains(txid)) {
|
||||
// // already scanned tx, continue to next tx
|
||||
// pos++;
|
||||
// continue;
|
||||
// }
|
||||
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();
|
||||
|
||||
final spb = SilentPaymentBuilder(receiverTweak: tweak);
|
||||
final result = spb.scanOutputs(
|
||||
scanData.silentAddress.b_scan,
|
||||
scanData.silentAddress.B_spend,
|
||||
outputPubkeys
|
||||
.map((output) =>
|
||||
BytesUtils.toHexString(BytesUtils.fromHexString(output.toString()).sublist(2))
|
||||
.toString())
|
||||
.toList(),
|
||||
precomputedLabels: scanData.labels,
|
||||
);
|
||||
|
||||
if (result.isEmpty) {
|
||||
// no results tx, continue to next tx
|
||||
continue;
|
||||
}
|
||||
|
||||
result.forEach((key, value) async {
|
||||
final t_k = value[0];
|
||||
final address = ECPublic.fromHex(key).toTaprootAddress().toAddress(scanData.network);
|
||||
|
||||
final listUnspent =
|
||||
await electrumClient.getListUnspentWithAddress(address, scanData.network);
|
||||
|
||||
BitcoinUnspent? info;
|
||||
await Future.forEach<Map<String, dynamic>>(listUnspent, (unspent) async {
|
||||
try {
|
||||
final addressRecord = BitcoinSilentPaymentAddressRecord(address,
|
||||
index: 0,
|
||||
isHidden: false,
|
||||
isUsed: true,
|
||||
network: scanData.network,
|
||||
silentPaymentTweak: t_k);
|
||||
info = BitcoinUnspent.fromJSON(addressRecord, unspent);
|
||||
} catch (_) {}
|
||||
});
|
||||
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// final tweak = value[0];
|
||||
// String? label;
|
||||
// if (value.length > 1) label = value[1];
|
||||
|
||||
final tx = info!;
|
||||
final txInfo = ElectrumTransactionInfo(
|
||||
WalletType.bitcoin,
|
||||
id: tx.hash,
|
||||
height: syncHeight,
|
||||
amount: 0, // will be added later via unspent
|
||||
fee: 0,
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: false,
|
||||
date: DateTime.now(),
|
||||
confirmations: currentChainTip - syncHeight - 1,
|
||||
to: scanData.silentAddress.toString(),
|
||||
unspents: [tx],
|
||||
final spb = SilentPaymentBuilder(receiverTweak: tweak);
|
||||
final addToWallet = spb.scanOutputs(
|
||||
scanData.silentAddress.b_scan,
|
||||
scanData.silentAddress.B_spend,
|
||||
outputPubkeys.values
|
||||
.map((o) => getScriptFromOutput(
|
||||
o["pubkey"].toString(), int.parse(o["amount"].toString())))
|
||||
.toList(),
|
||||
precomputedLabels: scanData.labels,
|
||||
);
|
||||
|
||||
// bool spent = false;
|
||||
// for (final s in status) {
|
||||
// if ((s["spent"] as bool) == true) {
|
||||
// spent = true;
|
||||
if (addToWallet.isEmpty) {
|
||||
// no results tx, continue to next tx
|
||||
continue;
|
||||
}
|
||||
|
||||
// scanData.sendPort.send({txid: txInfo});
|
||||
addToWallet.forEach((key, value) async {
|
||||
final t_k = value.tweak;
|
||||
|
||||
// final sentTxId = s["txid"] as String;
|
||||
// final sentTx = json.decode(
|
||||
// (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId")))
|
||||
// .body);
|
||||
final addressRecord = BitcoinSilentPaymentAddressRecord(
|
||||
value.output.address.toAddress(scanData.network),
|
||||
index: 0,
|
||||
isHidden: false,
|
||||
isUsed: true,
|
||||
network: scanData.network,
|
||||
silentPaymentTweak: t_k,
|
||||
type: SegwitAddresType.p2tr,
|
||||
);
|
||||
|
||||
// int amount = 0;
|
||||
// for (final out in (sentTx["vout"] as List<dynamic>)) {
|
||||
// amount += out["value"] as int;
|
||||
// }
|
||||
int? amount;
|
||||
int? pos;
|
||||
outputPubkeys.entries.firstWhere((k) {
|
||||
final matches = k.value["pubkey"] == key;
|
||||
if (matches) {
|
||||
amount = int.parse(k.value["amount"].toString());
|
||||
pos = int.parse(k.key.toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// final height = s["status"]["block_height"] as int;
|
||||
final json = <String, dynamic>{
|
||||
'address_record': addressRecord.toJSON(),
|
||||
'tx_hash': txid,
|
||||
'value': amount!,
|
||||
'tx_pos': pos!,
|
||||
'silent_payment_tweak': t_k,
|
||||
};
|
||||
|
||||
// scanData.sendPort.send({
|
||||
// sentTxId: ElectrumTransactionInfo(
|
||||
// WalletType.bitcoin,
|
||||
// id: sentTxId,
|
||||
// height: height,
|
||||
// amount: amount,
|
||||
// fee: 0,
|
||||
// direction: TransactionDirection.outgoing,
|
||||
// isPending: false,
|
||||
// date: DateTime.fromMillisecondsSinceEpoch(
|
||||
// (s["status"]["block_time"] as int) * 1000),
|
||||
// confirmations: currentChainTip - height,
|
||||
// )
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
final tx = BitcoinUnspent.fromJSON(addressRecord, json);
|
||||
|
||||
// if (spent) {
|
||||
// return;
|
||||
// }
|
||||
final txInfo = ElectrumTransactionInfo(
|
||||
WalletType.bitcoin,
|
||||
id: tx.hash,
|
||||
height: syncHeight,
|
||||
amount: 0, // will be added later via unspent
|
||||
fee: 0,
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: false,
|
||||
date: DateTime.now(),
|
||||
confirmations: currentChainTip - syncHeight - 1,
|
||||
to: value.label != null
|
||||
? SilentPaymentAddress(
|
||||
version: scanData.silentAddress.version,
|
||||
B_scan: scanData.silentAddress.B_scan
|
||||
.tweakAdd(BigintUtils.fromBytes(BytesUtils.fromHexString(value.tweak))),
|
||||
B_spend: scanData.silentAddress.B_spend,
|
||||
hrp: scanData.silentAddress.hrp,
|
||||
).toString()
|
||||
: scanData.silentAddress.toString(),
|
||||
unspents: [tx],
|
||||
);
|
||||
|
||||
// found utxo for tx, send unspent coin to main isolate
|
||||
// scanData.sendPort.send(txInfo);
|
||||
scanData.sendPort.send({txInfo.id: txInfo});
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
print([e, s]);
|
||||
}
|
||||
|
||||
// also send tx data for tx history
|
||||
scanData.sendPort.send({txInfo.id: txInfo});
|
||||
});
|
||||
} catch (_) {}
|
||||
// Finished scanning block, add 1 to height and continue to next block in loop
|
||||
syncHeight += 1;
|
||||
scanData.sendPort.send(SyncResponse(syncHeight,
|
||||
SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight)));
|
||||
}
|
||||
|
||||
// Finished scanning block, add 1 to height and continue to next block in loop
|
||||
syncHeight += 1;
|
||||
currentChainTip = await getNodeHeightOrUpdate(syncHeight);
|
||||
scanData.sendPort.send(SyncResponse(syncHeight,
|
||||
SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight)));
|
||||
} catch (e, stacktrace) {
|
||||
print(stacktrace);
|
||||
print(e.toString());
|
||||
|
@ -1348,13 +1349,13 @@ Future<void> startRefresh(ScanData scanData) async {
|
|||
class EstimatedTxResult {
|
||||
EstimatedTxResult(
|
||||
{required this.utxos,
|
||||
required this.privateKeys,
|
||||
required this.inputPrivKeyInfos,
|
||||
required this.fee,
|
||||
required this.amount,
|
||||
required this.spendsSilentPayment});
|
||||
|
||||
final List<UtxoWithAddress> utxos;
|
||||
final List<ECPrivate> privateKeys;
|
||||
final List<ECPrivateInfo> inputPrivKeyInfos;
|
||||
final int fee;
|
||||
final int amount;
|
||||
final bool spendsSilentPayment;
|
||||
|
|
|
@ -57,8 +57,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp');
|
||||
|
||||
if (silentAddresses.length == 0)
|
||||
silentAddresses.add(BitcoinSilentPaymentAddressRecord(silentAddress.toString(),
|
||||
index: 1, isHidden: false, name: "", silentPaymentTweak: null, network: network));
|
||||
silentAddresses.add(BitcoinSilentPaymentAddressRecord(
|
||||
silentAddress.toString(),
|
||||
index: 1,
|
||||
isHidden: false,
|
||||
name: "",
|
||||
silentPaymentTweak: null,
|
||||
network: network,
|
||||
type: SilentPaymentsAddresType.p2sp,
|
||||
));
|
||||
}
|
||||
|
||||
updateAddressesByMatch();
|
||||
|
@ -267,6 +274,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
silentPaymentTweak:
|
||||
BytesUtils.toHexString(silentAddress!.generateLabel(currentSilentAddressIndex)),
|
||||
network: network,
|
||||
type: SilentPaymentsAddresType.p2sp,
|
||||
);
|
||||
|
||||
silentAddresses.add(address);
|
||||
|
|
|
@ -80,7 +80,7 @@ packages:
|
|||
description:
|
||||
path: "."
|
||||
ref: cake-update-v2
|
||||
resolved-ref: f45e34d18ddff52764101a2ec96dcbc2be730555
|
||||
resolved-ref: fb4c0a0b6cf24628ddad7d3cdc58e4c918eff714
|
||||
url: "https://github.com/cake-tech/bitcoin_base"
|
||||
source: git
|
||||
version: "4.0.0"
|
||||
|
|
|
@ -63,6 +63,13 @@ class UnsupportedSyncStatus extends SyncStatus {
|
|||
double progress() => 1.0;
|
||||
}
|
||||
|
||||
class TimedOutSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
@override
|
||||
String toString() => 'Timed out';
|
||||
}
|
||||
|
||||
class LostConnectionSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
|
|
|
@ -40,5 +40,9 @@ String syncStatusTitle(SyncStatus syncStatus) {
|
|||
return S.current.sync_status_unsupported;
|
||||
}
|
||||
|
||||
if (syncStatus is TimedOutSyncStatus) {
|
||||
return S.current.sync_status_timed_out;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ import 'package:collection/collection.dart';
|
|||
|
||||
const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081';
|
||||
const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
|
||||
const publicBitcoinTestnetElectrumAddress = 'electrum.blockstream.info';
|
||||
const publicBitcoinTestnetElectrumPort = '60002';
|
||||
const publicBitcoinTestnetElectrumAddress = '198.58.111.154';
|
||||
const publicBitcoinTestnetElectrumPort = '50002';
|
||||
const publicBitcoinTestnetElectrumUri =
|
||||
'$publicBitcoinTestnetElectrumAddress:$publicBitcoinTestnetElectrumPort';
|
||||
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "بدء المزامنة",
|
||||
"sync_status_syncronized": "متزامن",
|
||||
"sync_status_syncronizing": "يتم المزامنة",
|
||||
"sync_status_timed_out": "نفد وقته",
|
||||
"sync_status_unsupported": "عقدة غير مدعومة",
|
||||
"syncing_wallet_alert_content": "قد لا يكتمل رصيدك وقائمة المعاملات الخاصة بك حتى تظهر عبارة “SYNCHRONIZED“ في الأعلى. انقر / اضغط لمعرفة المزيد.",
|
||||
"syncing_wallet_alert_title": "محفظتك تتم مزامنتها",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "ЗАПОЧВАНЕ НА СИНХРОНИЗАЦИЯ",
|
||||
"sync_status_syncronized": "СИНХРОНИЗИРАНО",
|
||||
"sync_status_syncronizing": "СИНХРОНИЗИРАНЕ",
|
||||
"sync_status_timed_out": "ВРЕМЕТО ИЗТЕЧЕ",
|
||||
"sync_status_unsupported": "Неподдържан възел",
|
||||
"syncing_wallet_alert_content": "Списъкът ви с баланс и транзакции може да не е пълен, докато в горната част не пише „СИНХРОНИЗИРАН“. Кликнете/докоснете, за да научите повече.",
|
||||
"syncing_wallet_alert_title": "Вашият портфейл се синхронизира",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "SPOUŠTĚNÍ SYNCHRONIZACE",
|
||||
"sync_status_syncronized": "SYNCHRONIZOVÁNO",
|
||||
"sync_status_syncronizing": "SYNCHRONIZUJI",
|
||||
"sync_status_timed_out": "ČAS VYPRŠEL",
|
||||
"sync_status_unsupported": "Nepodporovaný uzel",
|
||||
"syncing_wallet_alert_content": "Váš seznam zůstatků a transakcí nemusí být úplný, dokud nebude nahoře uvedeno „SYNCHRONIZOVANÉ“. Kliknutím/klepnutím se dozvíte více.",
|
||||
"syncing_wallet_alert_title": "Vaše peněženka se synchronizuje",
|
||||
|
|
|
@ -637,6 +637,7 @@
|
|||
"sync_status_starting_sync": "STARTE SYNCHRONISIERUNG",
|
||||
"sync_status_syncronized": "SYNCHRONISIERT",
|
||||
"sync_status_syncronizing": "SYNCHRONISIERE",
|
||||
"sync_status_timed_out": "Zeitlich abgestimmt",
|
||||
"sync_status_unsupported": "Nicht unterstützter Knoten",
|
||||
"syncing_wallet_alert_content": "Ihr Kontostand und Ihre Transaktionsliste sind möglicherweise erst vollständig, wenn oben „SYNCHRONISIERT“ steht. Klicken/tippen Sie, um mehr zu erfahren.",
|
||||
"syncing_wallet_alert_title": "Ihr Wallet wird synchronisiert",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "STARTING SYNC",
|
||||
"sync_status_syncronized": "SYNCHRONIZED",
|
||||
"sync_status_syncronizing": "SYNCHRONIZING",
|
||||
"sync_status_timed_out": "TIMED OUT",
|
||||
"sync_status_unsupported": "UNSUPPORTED NODE",
|
||||
"syncing_wallet_alert_content": "Your balance and transaction list may not be complete until it says “SYNCHRONIZED” at the top. Click/tap to learn more.",
|
||||
"syncing_wallet_alert_title": "Your wallet is syncing",
|
||||
|
|
|
@ -637,6 +637,7 @@
|
|||
"sync_status_starting_sync": "EMPEZANDO A SINCRONIZAR",
|
||||
"sync_status_syncronized": "SINCRONIZADO",
|
||||
"sync_status_syncronizing": "SINCRONIZANDO",
|
||||
"sync_status_timed_out": "CADUCADO",
|
||||
"sync_status_unsupported": "Nodo no compatible",
|
||||
"syncing_wallet_alert_content": "Es posible que su lista de saldo y transacciones no esté completa hasta que diga \"SINCRONIZADO\" en la parte superior. Haga clic/toque para obtener más información.",
|
||||
"syncing_wallet_alert_title": "Tu billetera se está sincronizando",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "DÉBUT DE SYNCHRO",
|
||||
"sync_status_syncronized": "SYNCHRONISÉ",
|
||||
"sync_status_syncronizing": "SYNCHRONISATION EN COURS",
|
||||
"sync_status_timed_out": "FIN DU TEMPS",
|
||||
"sync_status_unsupported": "Nœud non pris en charge",
|
||||
"syncing_wallet_alert_content": "Votre solde et votre liste de transactions peuvent ne pas être à jour tant que la mention « SYNCHRONISÉ » n'apparaît en haut de l'écran. Cliquez/appuyez pour en savoir plus.",
|
||||
"syncing_wallet_alert_title": "Votre portefeuille (wallet) est en cours de synchronisation",
|
||||
|
|
|
@ -638,6 +638,7 @@
|
|||
"sync_status_starting_sync": "KWAFI",
|
||||
"sync_status_syncronized": "KYAU",
|
||||
"sync_status_syncronizing": "KWAFI",
|
||||
"sync_status_timed_out": "ATED Out",
|
||||
"sync_status_unsupported": "Ba a Taimako ba",
|
||||
"syncing_wallet_alert_content": "Ma'aunin ku da lissafin ma'amala bazai cika ba har sai an ce \"SYNCHRONIZED\" a saman. Danna/matsa don ƙarin koyo.",
|
||||
"syncing_wallet_alert_title": "Walat ɗin ku yana aiki tare",
|
||||
|
|
|
@ -638,6 +638,7 @@
|
|||
"sync_status_starting_sync": "सिताज़ा करना",
|
||||
"sync_status_syncronized": "सिंक्रनाइज़",
|
||||
"sync_status_syncronizing": "सिंक्रनाइज़ करने",
|
||||
"sync_status_timed_out": "समय समााप्त",
|
||||
"sync_status_unsupported": "असमर्थित नोड",
|
||||
"syncing_wallet_alert_content": "आपकी शेष राशि और लेनदेन सूची तब तक पूरी नहीं हो सकती जब तक कि शीर्ष पर \"सिंक्रनाइज़्ड\" न लिखा हो। अधिक जानने के लिए क्लिक/टैप करें।",
|
||||
"syncing_wallet_alert_title": "आपका वॉलेट सिंक हो रहा है",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "ZAPOČINJEMO SINKRONIZIRANJE",
|
||||
"sync_status_syncronized": "SINKRONIZIRANO",
|
||||
"sync_status_syncronizing": "SINKRONIZIRANJE",
|
||||
"sync_status_timed_out": "ISTEKLO",
|
||||
"sync_status_unsupported": "Nepodržani čvor",
|
||||
"syncing_wallet_alert_content": "Vaš saldo i popis transakcija možda neće biti potpuni sve dok na vrhu ne piše \"SINKRONIZIRANO\". Kliknite/dodirnite da biste saznali više.",
|
||||
"syncing_wallet_alert_title": "Vaš novčanik se sinkronizira",
|
||||
|
|
|
@ -639,6 +639,7 @@
|
|||
"sync_status_starting_sync": "MULAI SINKRONISASI",
|
||||
"sync_status_syncronized": "SUDAH TERSINKRONISASI",
|
||||
"sync_status_syncronizing": "SEDANG SINKRONISASI",
|
||||
"sync_status_timed_out": "WAKTU HABIS",
|
||||
"sync_status_unsupported": "Node yang tidak didukung",
|
||||
"syncing_wallet_alert_content": "Saldo dan daftar transaksi Anda mungkin belum lengkap sampai tertulis “SYNCHRONIZED” di bagian atas. Klik/ketuk untuk mempelajari lebih lanjut.",
|
||||
"syncing_wallet_alert_title": "Dompet Anda sedang disinkronkan",
|
||||
|
|
|
@ -638,6 +638,7 @@
|
|||
"sync_status_starting_sync": "INIZIO SINC",
|
||||
"sync_status_syncronized": "SINCRONIZZATO",
|
||||
"sync_status_syncronizing": "SINCRONIZZAZIONE",
|
||||
"sync_status_timed_out": "FUORI TEMPO",
|
||||
"sync_status_unsupported": "Nodo non supportato",
|
||||
"syncing_wallet_alert_content": "Il saldo e l'elenco delle transazioni potrebbero non essere completi fino a quando non viene visualizzato \"SYNCHRONIZED\" in alto. Clicca/tocca per saperne di più.",
|
||||
"syncing_wallet_alert_title": "Il tuo portafoglio si sta sincronizzando",
|
||||
|
|
|
@ -637,6 +637,7 @@
|
|||
"sync_status_starting_sync": "同期の開始",
|
||||
"sync_status_syncronized": "同期された",
|
||||
"sync_status_syncronizing": "同期",
|
||||
"sync_status_timed_out": "タイムアウトしました",
|
||||
"sync_status_unsupported": "サポートされていないノード",
|
||||
"syncing_wallet_alert_content": "上部に「同期済み」と表示されるまで、残高と取引リストが完了していない可能性があります。詳細については、クリック/タップしてください。",
|
||||
"syncing_wallet_alert_title": "ウォレットは同期中です",
|
||||
|
|
|
@ -637,6 +637,7 @@
|
|||
"sync_status_starting_sync": "동기화 시작",
|
||||
"sync_status_syncronized": "동기화",
|
||||
"sync_status_syncronizing": "동기화",
|
||||
"sync_status_timed_out": "시간 초과",
|
||||
"sync_status_unsupported": "지원되지 않은 노드",
|
||||
"syncing_wallet_alert_content": "상단에 \"동기화됨\"이라고 표시될 때까지 잔액 및 거래 목록이 완전하지 않을 수 있습니다. 자세히 알아보려면 클릭/탭하세요.",
|
||||
"syncing_wallet_alert_title": "지갑 동기화 중",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "စင့်ခ်လုပ်ခြင်း။",
|
||||
"sync_status_syncronized": "ထပ်တူပြုထားသည်။",
|
||||
"sync_status_syncronizing": "ထပ်တူပြုခြင်း။",
|
||||
"sync_status_timed_out": "ထွက်အချိန်ကုန်",
|
||||
"sync_status_unsupported": "node မထောက်ပံ့ node ကို",
|
||||
"syncing_wallet_alert_content": "သင်၏လက်ကျန်နှင့် ငွေပေးငွေယူစာရင်းသည် ထိပ်တွင် \"Synchronizeed\" ဟုပြောသည်အထိ မပြီးမြောက်နိုင်ပါ။ ပိုမိုလေ့လာရန် နှိပ်/နှိပ်ပါ။",
|
||||
"syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "BEGINNEN MET SYNCHRONISEREN",
|
||||
"sync_status_syncronized": "SYNCHRONIZED",
|
||||
"sync_status_syncronizing": "SYNCHRONISEREN",
|
||||
"sync_status_timed_out": "Uitgeput",
|
||||
"sync_status_unsupported": "Niet ondersteund knooppunt",
|
||||
"syncing_wallet_alert_content": "Uw saldo- en transactielijst is mogelijk pas compleet als er bovenaan 'GESYNCHRONISEERD' staat. Klik/tik voor meer informatie.",
|
||||
"syncing_wallet_alert_title": "Uw portemonnee wordt gesynchroniseerd",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "ROZPOCZĘCIE SYNCHRONIZACJI",
|
||||
"sync_status_syncronized": "ZSYNCHRONIZOWANO",
|
||||
"sync_status_syncronizing": "SYNCHRONIZACJA",
|
||||
"sync_status_timed_out": "PRZEKROCZONO LIMIT CZASU",
|
||||
"sync_status_unsupported": "Nieobsługiwany węzeł",
|
||||
"syncing_wallet_alert_content": "Twoje saldo i lista transakcji mogą nie być kompletne, dopóki u góry nie pojawi się napis „SYNCHRONIZOWANY”. Kliknij/stuknij, aby dowiedzieć się więcej.",
|
||||
"syncing_wallet_alert_title": "Twój portfel się synchronizuje",
|
||||
|
|
|
@ -638,6 +638,7 @@
|
|||
"sync_status_starting_sync": "INICIANDO SINCRONIZAÇÃO",
|
||||
"sync_status_syncronized": "SINCRONIZADO",
|
||||
"sync_status_syncronizing": "SINCRONIZANDO",
|
||||
"sync_status_timed_out": "TEMPO ESGOTADO",
|
||||
"sync_status_unsupported": "Nó não suportado",
|
||||
"syncing_wallet_alert_content": "Seu saldo e lista de transações podem não estar completos até que diga “SYNCHRONIZED” no topo. Clique/toque para saber mais.",
|
||||
"syncing_wallet_alert_title": "Sua carteira está sincronizando",
|
||||
|
|
|
@ -637,6 +637,7 @@
|
|||
"sync_status_starting_sync": "НАЧАЛО СИНХРОНИЗАЦИИ",
|
||||
"sync_status_syncronized": "СИНХРОНИЗИРОВАН",
|
||||
"sync_status_syncronizing": "СИНХРОНИЗАЦИЯ",
|
||||
"sync_status_timed_out": "ВРЕМЯ ВЫШЛО",
|
||||
"sync_status_unsupported": "Неподдерживаемый узел",
|
||||
"syncing_wallet_alert_content": "Ваш баланс и список транзакций могут быть неполными, пока вверху не будет написано «СИНХРОНИЗИРОВАНО». Щелкните/коснитесь, чтобы узнать больше.",
|
||||
"syncing_wallet_alert_title": "Ваш кошелек синхронизируется",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "กำลังเริ่มซิงโครไนซ์",
|
||||
"sync_status_syncronized": "ซิงโครไนซ์แล้ว",
|
||||
"sync_status_syncronizing": "กำลังซิงโครไนซ์",
|
||||
"sync_status_timed_out": "หมดเวลา",
|
||||
"sync_status_unsupported": "โหนดที่ไม่ได้รับการสนับสนุน",
|
||||
"syncing_wallet_alert_content": "รายการยอดเงินและธุรกรรมของคุณอาจไม่สมบูรณ์จนกว่าจะมีข้อความว่า “ซิงโครไนซ์” ที่ด้านบน คลิก/แตะเพื่อเรียนรู้เพิ่มเติม่",
|
||||
"syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "Simula sa pag -sync",
|
||||
"sync_status_syncronized": "Naka -synchronize",
|
||||
"sync_status_syncronizing": "Pag -synchronize",
|
||||
"sync_status_timed_out": "Nag -time out",
|
||||
"sync_status_unsupported": "Hindi suportadong node",
|
||||
"syncing_wallet_alert_content": "Ang iyong balanse at listahan ng transaksyon ay maaaring hindi kumpleto hanggang sa sabihin nito na \"naka -synchronize\" sa tuktok. Mag -click/tap upang malaman ang higit pa.",
|
||||
"syncing_wallet_alert_title": "Ang iyong pitaka ay nag -sync",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "SENKRONİZE BAŞLATILIYOR",
|
||||
"sync_status_syncronized": "SENKRONİZE EDİLDİ",
|
||||
"sync_status_syncronizing": "SENKRONİZE EDİLİYOR",
|
||||
"sync_status_timed_out": "ZAMAN AŞIMINA UĞRADI",
|
||||
"sync_status_unsupported": "Desteklenmeyen düğüm",
|
||||
"syncing_wallet_alert_content": "Bakiyeniz ve işlem listeniz, en üstte \"SENKRONİZE EDİLDİ\" yazana kadar tamamlanmamış olabilir. Daha fazla bilgi edinmek için tıklayın/dokunun.",
|
||||
"syncing_wallet_alert_title": "Cüzdanınız senkronize ediliyor",
|
||||
|
|
|
@ -637,6 +637,7 @@
|
|||
"sync_status_starting_sync": "ПОЧАТОК СИНХРОНІЗАЦІЇ",
|
||||
"sync_status_syncronized": "СИНХРОНІЗОВАНИЙ",
|
||||
"sync_status_syncronizing": "СИНХРОНІЗАЦІЯ",
|
||||
"sync_status_timed_out": "ТАЙМ-АУТ",
|
||||
"sync_status_unsupported": "Непідтримуваний вузол",
|
||||
"syncing_wallet_alert_content": "Ваш баланс та список транзакцій може бути неповним, доки вгорі не буде написано «СИНХРОНІЗОВАНО». Натисніть/торкніться, щоб дізнатися більше.",
|
||||
"syncing_wallet_alert_title": "Ваш гаманець синхронізується",
|
||||
|
|
|
@ -638,6 +638,7 @@
|
|||
"sync_status_starting_sync": "مطابقت پذیری شروع کر رہا ہے۔",
|
||||
"sync_status_syncronized": "مطابقت پذیر",
|
||||
"sync_status_syncronizing": "مطابقت پذیری",
|
||||
"sync_status_timed_out": "وقت ختم",
|
||||
"sync_status_unsupported": "غیر تعاون یافتہ نوڈ",
|
||||
"syncing_wallet_alert_content": "آپ کے بیلنس اور لین دین کی فہرست اس وقت تک مکمل نہیں ہو سکتی جب تک کہ یہ سب سے اوپر \"SYNCRONIZED\" نہ کہے۔ مزید جاننے کے لیے کلک/تھپتھپائیں۔",
|
||||
"syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔",
|
||||
|
|
|
@ -637,6 +637,7 @@
|
|||
"sync_status_starting_sync": "Ń BẸ̀RẸ̀ RẸ́",
|
||||
"sync_status_syncronized": "TI MÚDỌ́GBA",
|
||||
"sync_status_syncronizing": "Ń MÚDỌ́GBA",
|
||||
"sync_status_timed_out": "Ti akoko jade",
|
||||
"sync_status_unsupported": "Ile-igbimọ ti ko ni atilẹyin",
|
||||
"syncing_wallet_alert_content": "Iwontunws.funfun rẹ ati atokọ idunadura le ma pari titi ti yoo fi sọ “SYNCHRONIZED” ni oke. Tẹ/tẹ ni kia kia lati ni imọ siwaju sii.",
|
||||
"syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ",
|
||||
|
|
|
@ -636,6 +636,7 @@
|
|||
"sync_status_starting_sync": "开始同步",
|
||||
"sync_status_syncronized": "已同步",
|
||||
"sync_status_syncronizing": "正在同步",
|
||||
"sync_status_timed_out": "时间到",
|
||||
"sync_status_unsupported": "不支持的节点",
|
||||
"syncing_wallet_alert_content": "您的余额和交易列表可能不完整,直到顶部显示“已同步”。单击/点击以了解更多信息。",
|
||||
"syncing_wallet_alert_title": "您的钱包正在同步",
|
||||
|
|
Loading…
Reference in a new issue