mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-28 18:19:13 +00:00
Merge branch 'mweb_enhancements_4' of https://github.com/cake-tech/cake_wallet into mweb_enhancements_4
This commit is contained in:
commit
8dc1ed6363
6 changed files with 124 additions and 84 deletions
cw_bitcoin/lib
cw_core/lib
cw_mweb/lib
res/values
|
@ -52,10 +52,9 @@ part 'electrum_wallet.g.dart';
|
|||
|
||||
class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet;
|
||||
|
||||
abstract class ElectrumWalletBase extends WalletBase<
|
||||
ElectrumBalance,
|
||||
ElectrumTransactionHistory,
|
||||
ElectrumTransactionInfo> with Store, WalletKeysFile {
|
||||
abstract class ElectrumWalletBase
|
||||
extends WalletBase<ElectrumBalance, ElectrumTransactionHistory, ElectrumTransactionInfo>
|
||||
with Store, WalletKeysFile {
|
||||
ElectrumWalletBase({
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
|
@ -71,8 +70,8 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
ElectrumBalance? initialBalance,
|
||||
CryptoCurrency? currency,
|
||||
this.alwaysScan,
|
||||
}) : accountHD = getAccountHDWallet(
|
||||
currency, network, seedBytes, xpub, walletInfo.derivationInfo),
|
||||
}) : accountHD =
|
||||
getAccountHDWallet(currency, network, seedBytes, xpub, walletInfo.derivationInfo),
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_feeRates = <int>[],
|
||||
|
@ -107,12 +106,8 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
sharedPrefs.complete(SharedPreferences.getInstance());
|
||||
}
|
||||
|
||||
static Bip32Slip10Secp256k1 getAccountHDWallet(
|
||||
CryptoCurrency? currency,
|
||||
BasedUtxoNetwork network,
|
||||
Uint8List? seedBytes,
|
||||
String? xpub,
|
||||
DerivationInfo? derivationInfo) {
|
||||
static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network,
|
||||
Uint8List? seedBytes, String? xpub, DerivationInfo? derivationInfo) {
|
||||
if (seedBytes == null && xpub == null) {
|
||||
throw Exception(
|
||||
"To create a Wallet you need either a seed or an xpub. This should not happen");
|
||||
|
@ -123,9 +118,8 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
case CryptoCurrency.btc:
|
||||
case CryptoCurrency.ltc:
|
||||
case CryptoCurrency.tbtc:
|
||||
return Bip32Slip10Secp256k1.fromSeed(seedBytes, getKeyNetVersion(network))
|
||||
.derivePath(_hardenedDerivationPath(
|
||||
derivationInfo?.derivationPath ?? electrum_path))
|
||||
return Bip32Slip10Secp256k1.fromSeed(seedBytes, getKeyNetVersion(network)).derivePath(
|
||||
_hardenedDerivationPath(derivationInfo?.derivationPath ?? electrum_path))
|
||||
as Bip32Slip10Secp256k1;
|
||||
case CryptoCurrency.bch:
|
||||
return bitcoinCashHDWallet(seedBytes);
|
||||
|
@ -134,13 +128,11 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
}
|
||||
}
|
||||
|
||||
return Bip32Slip10Secp256k1.fromExtendedKey(
|
||||
xpub!, getKeyNetVersion(network));
|
||||
return Bip32Slip10Secp256k1.fromExtendedKey(xpub!, getKeyNetVersion(network));
|
||||
}
|
||||
|
||||
static Bip32Slip10Secp256k1 bitcoinCashHDWallet(Uint8List seedBytes) =>
|
||||
Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'")
|
||||
as Bip32Slip10Secp256k1;
|
||||
Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'") as Bip32Slip10Secp256k1;
|
||||
|
||||
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
||||
inputsCount * 68 + outputsCounts * 34 + 10;
|
||||
|
@ -357,7 +349,7 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
isSingleScan: doSingleScan ?? false,
|
||||
));
|
||||
|
||||
_receiveStream?.cancel();
|
||||
await _receiveStream?.cancel();
|
||||
_receiveStream = receivePort.listen((var message) async {
|
||||
if (message is Map<String, ElectrumTransactionInfo>) {
|
||||
for (final map in message.entries) {
|
||||
|
@ -577,6 +569,29 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
return node!.supportsSilentPayments!;
|
||||
}
|
||||
|
||||
Future<bool> getNodeSupportsMweb() async {
|
||||
final version = await electrumClient.version();
|
||||
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version.isNotEmpty) {
|
||||
final server = version[0];
|
||||
print("server: $server");
|
||||
|
||||
return false;
|
||||
|
||||
// if (server.toLowerCase().contains('electrs')) {
|
||||
// node!.isElectrs = true;
|
||||
// node!.save();
|
||||
// return node!.isElectrs!;
|
||||
// }
|
||||
}
|
||||
|
||||
return node!.supportsMweb!;
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> connectToNode({required Node node}) async {
|
||||
|
@ -652,9 +667,8 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
ECPrivate? privkey;
|
||||
bool? isSilentPayment = false;
|
||||
|
||||
final hd = utx.bitcoinAddressRecord.isHidden
|
||||
? walletAddresses.sideHd
|
||||
: walletAddresses.mainHd;
|
||||
final hd =
|
||||
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
|
||||
|
||||
if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
||||
final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||
|
@ -1234,8 +1248,7 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
}
|
||||
}
|
||||
|
||||
void setLedgerConnection(ledger.LedgerConnection connection) =>
|
||||
throw UnimplementedError();
|
||||
void setLedgerConnection(ledger.LedgerConnection connection) => throw UnimplementedError();
|
||||
|
||||
Future<BtcTransaction> buildHardwareWalletTransaction({
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
|
@ -1594,9 +1607,7 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
|
||||
final btcAddress = RegexUtils.addressTypeFromStr(addressRecord.address, network);
|
||||
final privkey = generateECPrivate(
|
||||
hd: addressRecord.isHidden
|
||||
? walletAddresses.sideHd
|
||||
: walletAddresses.mainHd,
|
||||
hd: addressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
||||
index: addressRecord.index,
|
||||
network: network);
|
||||
|
||||
|
@ -1778,8 +1789,7 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
|
||||
if (height != null) {
|
||||
if (time == null && height > 0) {
|
||||
time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000)
|
||||
.round();
|
||||
time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000).round();
|
||||
}
|
||||
|
||||
if (confirmations == null) {
|
||||
|
@ -2044,6 +2054,8 @@ abstract class ElectrumWalletBase extends WalletBase<
|
|||
library: this.runtimeType.toString(),
|
||||
));
|
||||
}
|
||||
}, onError: (e, s) {
|
||||
print("sub_listen error: $e $s");
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -331,7 +331,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
);
|
||||
|
||||
silentAddresses.add(address);
|
||||
updateAddressesByMatch();
|
||||
Future.delayed(Duration.zero, () => updateAddressesByMatch());
|
||||
|
||||
return address;
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
network: network,
|
||||
);
|
||||
_addresses.add(address);
|
||||
updateAddressesByMatch();
|
||||
Future.delayed(Duration.zero, () => updateAddressesByMatch());
|
||||
return address;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:crypto/crypto.dart';
|
|||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/mweb_utxo.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_mweb/mwebd.pbgrpc.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
@ -287,6 +288,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
await (walletAddresses as LitecoinWalletAddresses).ensureMwebAddressUpToIndexExists(1020);
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> connectToNode({required Node node}) async {
|
||||
await super.connectToNode(node: node);
|
||||
if (await getNodeSupportsMweb()) {
|
||||
await CwMweb.setNodeUriOverride(node.uri.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
|
@ -582,55 +592,60 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
if (responseStream == null) {
|
||||
throw Exception("failed to get utxos stream!");
|
||||
}
|
||||
_utxoStream = responseStream.listen((Utxo sUtxo) async {
|
||||
// we're processing utxos, so our balance could still be innacurate:
|
||||
if (mwebSyncStatus is! SyncronizingSyncStatus && mwebSyncStatus is! SyncingSyncStatus) {
|
||||
mwebSyncStatus = SyncronizingSyncStatus();
|
||||
processingUtxos = true;
|
||||
_processingTimer?.cancel();
|
||||
_processingTimer = Timer.periodic(const Duration(seconds: 2), (timer) async {
|
||||
processingUtxos = false;
|
||||
timer.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
final utxo = MwebUtxo(
|
||||
address: sUtxo.address,
|
||||
blockTime: sUtxo.blockTime,
|
||||
height: sUtxo.height,
|
||||
outputId: sUtxo.outputId,
|
||||
value: sUtxo.value.toInt(),
|
||||
);
|
||||
|
||||
if (mwebUtxosBox.containsKey(utxo.outputId)) {
|
||||
// we've already stored this utxo, skip it:
|
||||
// but do update the utxo height if it's somehow different:
|
||||
final existingUtxo = mwebUtxosBox.get(utxo.outputId);
|
||||
if (existingUtxo!.height != utxo.height) {
|
||||
print(
|
||||
"updating utxo height for $utxo.outputId: ${existingUtxo.height} -> ${utxo.height}");
|
||||
existingUtxo.height = utxo.height;
|
||||
await mwebUtxosBox.put(utxo.outputId, existingUtxo);
|
||||
_utxoStream = responseStream.listen(
|
||||
(Utxo sUtxo) async {
|
||||
// we're processing utxos, so our balance could still be innacurate:
|
||||
if (mwebSyncStatus is! SyncronizingSyncStatus && mwebSyncStatus is! SyncingSyncStatus) {
|
||||
mwebSyncStatus = SyncronizingSyncStatus();
|
||||
processingUtxos = true;
|
||||
_processingTimer?.cancel();
|
||||
_processingTimer = Timer.periodic(const Duration(seconds: 2), (timer) async {
|
||||
processingUtxos = false;
|
||||
timer.cancel();
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await updateUnspent();
|
||||
await updateBalance();
|
||||
final utxo = MwebUtxo(
|
||||
address: sUtxo.address,
|
||||
blockTime: sUtxo.blockTime,
|
||||
height: sUtxo.height,
|
||||
outputId: sUtxo.outputId,
|
||||
value: sUtxo.value.toInt(),
|
||||
);
|
||||
|
||||
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
|
||||
if (mwebUtxosBox.containsKey(utxo.outputId)) {
|
||||
// we've already stored this utxo, skip it:
|
||||
// but do update the utxo height if it's somehow different:
|
||||
final existingUtxo = mwebUtxosBox.get(utxo.outputId);
|
||||
if (existingUtxo!.height != utxo.height) {
|
||||
print(
|
||||
"updating utxo height for $utxo.outputId: ${existingUtxo.height} -> ${utxo.height}");
|
||||
existingUtxo.height = utxo.height;
|
||||
await mwebUtxosBox.put(utxo.outputId, existingUtxo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// don't process utxos with addresses that are not in the mwebAddrs list:
|
||||
if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) {
|
||||
return;
|
||||
}
|
||||
await updateUnspent();
|
||||
await updateBalance();
|
||||
|
||||
await mwebUtxosBox.put(utxo.outputId, utxo);
|
||||
final mwebAddrs = (walletAddresses as LitecoinWalletAddresses).mwebAddrs;
|
||||
|
||||
await handleIncoming(utxo);
|
||||
}, onError: (error) {
|
||||
print("error in utxo stream: $error");
|
||||
});
|
||||
// don't process utxos with addresses that are not in the mwebAddrs list:
|
||||
if (utxo.address.isNotEmpty && !mwebAddrs.contains(utxo.address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await mwebUtxosBox.put(utxo.outputId, utxo);
|
||||
|
||||
await handleIncoming(utxo);
|
||||
},
|
||||
onError: (error) {
|
||||
print("error in utxo stream: $error");
|
||||
mwebSyncStatus = FailedSyncStatus(error: error.toString());
|
||||
},
|
||||
cancelOnError: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> checkMwebUtxosSpent() async {
|
||||
|
|
|
@ -79,6 +79,9 @@ class Node extends HiveObject with Keyable {
|
|||
@HiveField(9)
|
||||
bool? supportsSilentPayments;
|
||||
|
||||
@HiveField(10)
|
||||
bool? supportsMweb;
|
||||
|
||||
bool get isSSL => useSSL ?? false;
|
||||
|
||||
bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
|
||||
|
|
|
@ -15,6 +15,16 @@ class CwMweb {
|
|||
static int? _port;
|
||||
static const TIMEOUT_DURATION = Duration(seconds: 15);
|
||||
static Timer? logTimer;
|
||||
static String? nodeUriOverride;
|
||||
|
||||
|
||||
static Future<void> setNodeUriOverride(String uri) async {
|
||||
nodeUriOverride = uri;
|
||||
if (_rpcClient != null) {
|
||||
await stop();
|
||||
await _initializeClient();
|
||||
}
|
||||
}
|
||||
|
||||
static void readFileWithTimer(String filePath) {
|
||||
final file = File(filePath);
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
"billing_address_info": "Si se le solicita una dirección de facturación, proporcione su dirección de envío",
|
||||
"biometric_auth_reason": "Escanee su huella digital para autenticar",
|
||||
"bitcoin_dark_theme": "Tema oscuro de Bitcoin",
|
||||
"bitcoin_light_theme": "Tema de la luz de Bitcoin",
|
||||
"bitcoin_light_theme": "Tema claro de Bitcoin",
|
||||
"bitcoin_payments_require_1_confirmation": "Los pagos de Bitcoin requieren 1 confirmación, que puede demorar 20 minutos o más. ¡Gracias por su paciencia! Se le enviará un correo electrónico cuando se confirme el pago.",
|
||||
"block_remaining": "1 bloqueo restante",
|
||||
"Blocks_remaining": "${status} Bloques restantes",
|
||||
|
@ -93,7 +93,7 @@
|
|||
"buy_with": "Compra con",
|
||||
"by_cake_pay": "por Cake Pay",
|
||||
"cake_2fa_preset": "Pastel 2FA preestablecido",
|
||||
"cake_dark_theme": "Tema oscuro del pastel",
|
||||
"cake_dark_theme": "Tema oscuro",
|
||||
"cake_pay_account_note": "Regístrese con solo una dirección de correo electrónico para ver y comprar tarjetas. ¡Algunas incluso están disponibles con descuento!",
|
||||
"cake_pay_learn_more": "¡Compre y canjee tarjetas de regalo al instante en la aplicación!\nDeslice el dedo de izquierda a derecha para obtener más información.",
|
||||
"cake_pay_save_order": "La tarjeta debe enviarse a su correo electrónico dentro de 1 día hábil \n Guardar su ID de pedido:",
|
||||
|
@ -144,9 +144,9 @@
|
|||
"confirm_delete_wallet": "Esta acción eliminará esta billetera. ¿Desea continuar?",
|
||||
"confirm_fee_deduction": "Confirmar la deducción de la tarifa",
|
||||
"confirm_fee_deduction_content": "¿Acepta deducir la tarifa de la producción?",
|
||||
"confirm_passphrase": "Confirmar la frase de pases",
|
||||
"confirm_passphrase": "Confirmar la contraseña",
|
||||
"confirm_sending": "Confirmar envío",
|
||||
"confirm_silent_payments_switch_node": "Su nodo actual no admite pagos silenciosos \\ ncake billet cambiará a un nodo compatible, solo para escanear",
|
||||
"confirm_silent_payments_switch_node": "Su nodo actual no admite pagos silenciosos \\ nCake cambiará a un nodo compatible, solo para escanear",
|
||||
"confirmations": "Confirmaciones",
|
||||
"confirmed": "Saldo confirmado",
|
||||
"confirmed_tx": "Confirmado",
|
||||
|
@ -281,7 +281,7 @@
|
|||
"etherscan_history": "historia de etherscan",
|
||||
"event": "Evento",
|
||||
"events": "Eventos",
|
||||
"exchange": "Intercambio",
|
||||
"exchange": "Intercambiar",
|
||||
"exchange_incorrect_current_wallet_for_xmr": "Si desea intercambiar XMR desde su billetera de pastel Monero Balance, primero cambie a su billetera Monero.",
|
||||
"exchange_new_template": "Nueva plantilla",
|
||||
"exchange_provider_unsupported": "¡${providerName} ya no es compatible!",
|
||||
|
@ -373,8 +373,8 @@
|
|||
"litecoin_mweb_display_card": "Mostrar tarjeta MWEB",
|
||||
"litecoin_mweb_enable_later": "Puede elegir habilitar MWEB nuevamente en la configuración de visualización.",
|
||||
"litecoin_mweb_logs": "Registros de mweb",
|
||||
"litecoin_mweb_pegin": "Meter",
|
||||
"litecoin_mweb_pegout": "Estirar la pata",
|
||||
"litecoin_mweb_pegin": "Convertir",
|
||||
"litecoin_mweb_pegout": "Recuperar",
|
||||
"litecoin_mweb_scanning": "Escaneo mweb",
|
||||
"litecoin_mweb_settings": "Configuración de MWEB",
|
||||
"litecoin_mweb_warning": "El uso de MWEB inicialmente descargará ~ 600 MB de datos, y puede tomar hasta 30 minutos según la velocidad de la red. Estos datos iniciales solo se descargarán una vez y estarán disponibles para todas las billeteras de Litecoin",
|
||||
|
@ -827,7 +827,7 @@
|
|||
"transaction_priority_slow": "Lento",
|
||||
"transaction_sent": "Transacción enviada!",
|
||||
"transaction_sent_notice": "Si la pantalla no continúa después de 1 minuto, revisa un explorador de bloques y tu correo electrónico.",
|
||||
"transactions": "Actas",
|
||||
"transactions": "Transacciones",
|
||||
"transactions_by_date": "Transacciones por fecha",
|
||||
"trongrid_history": "Historia trongrid",
|
||||
"trusted": "de confianza",
|
||||
|
@ -940,4 +940,4 @@
|
|||
"you_will_get": "Convertir a",
|
||||
"you_will_send": "Convertir de",
|
||||
"yy": "YY"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue