mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
feat: allow scanning elect-rs using get_tweaks
This commit is contained in:
parent
e4703a9ace
commit
71201d4dee
46 changed files with 579 additions and 609 deletions
|
@ -23,9 +23,28 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
BitcoinReceivePageOption.p2sh,
|
BitcoinReceivePageOption.p2sh,
|
||||||
BitcoinReceivePageOption.p2tr,
|
BitcoinReceivePageOption.p2tr,
|
||||||
BitcoinReceivePageOption.p2wsh,
|
BitcoinReceivePageOption.p2wsh,
|
||||||
BitcoinReceivePageOption.p2pkh
|
BitcoinReceivePageOption.p2pkh,
|
||||||
|
BitcoinReceivePageOption.silent_payments,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
BitcoinAddressType toType() {
|
||||||
|
switch (this) {
|
||||||
|
case BitcoinReceivePageOption.p2tr:
|
||||||
|
return SegwitAddresType.p2tr;
|
||||||
|
case BitcoinReceivePageOption.p2wsh:
|
||||||
|
return SegwitAddresType.p2wsh;
|
||||||
|
case BitcoinReceivePageOption.p2pkh:
|
||||||
|
return P2pkhAddressType.p2pkh;
|
||||||
|
case BitcoinReceivePageOption.p2sh:
|
||||||
|
return P2shAddressType.p2wpkhInP2sh;
|
||||||
|
case BitcoinReceivePageOption.silent_payments:
|
||||||
|
return SilentPaymentsAddresType.p2sp;
|
||||||
|
case BitcoinReceivePageOption.p2wpkh:
|
||||||
|
default:
|
||||||
|
return SegwitAddresType.p2wpkh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
|
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SegwitAddresType.p2tr:
|
case SegwitAddresType.p2tr:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
@ -37,7 +38,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
networkType: bitcoin.bitcoin,
|
networkType: networkParam == null
|
||||||
|
? bitcoin.bitcoin
|
||||||
|
: networkParam == BitcoinNetwork.mainnet
|
||||||
|
? bitcoin.bitcoin
|
||||||
|
: bitcoin.testnet,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
|
@ -64,6 +69,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
|
String? addressPageType,
|
||||||
|
BasedUtxoNetwork? network,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
List<BitcoinAddressRecord>? initialSilentAddresses,
|
List<BitcoinAddressRecord>? initialSilentAddresses,
|
||||||
ElectrumBalance? initialBalance,
|
ElectrumBalance? initialBalance,
|
||||||
|
@ -71,6 +78,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
Map<String, int>? initialChangeAddressIndex,
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
int initialSilentAddressIndex = 0,
|
int initialSilentAddressIndex = 0,
|
||||||
}) async {
|
}) async {
|
||||||
|
final seedBytes = await mnemonicToSeedBytes(mnemonic);
|
||||||
return BitcoinWallet(
|
return BitcoinWallet(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
|
@ -79,10 +87,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialSilentAddresses: initialSilentAddresses,
|
initialSilentAddresses: initialSilentAddresses,
|
||||||
initialSilentAddressIndex: initialSilentAddressIndex,
|
initialSilentAddressIndex: initialSilentAddressIndex,
|
||||||
silentAddress: await SilentPaymentOwner.fromMnemonic(mnemonic,
|
silentAddress: await SilentPaymentOwner.fromPrivateKeys(
|
||||||
|
scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
|
||||||
|
seedBytes,
|
||||||
|
network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
|
||||||
|
).derivePath(SCAN_PATH).privKey!),
|
||||||
|
spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
|
||||||
|
seedBytes,
|
||||||
|
network: network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
|
||||||
|
).derivePath(SPEND_PATH).privKey!),
|
||||||
hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'),
|
hrp: network == BitcoinNetwork.testnet ? 'tsp' : 'sp'),
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
seedBytes: seedBytes,
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
addressPageType: addressPageType,
|
addressPageType: addressPageType,
|
||||||
|
@ -96,7 +112,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password);
|
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password,
|
||||||
|
walletInfo.network != null ? BasedUtxoNetwork.fromName(walletInfo.network!) : null);
|
||||||
|
|
||||||
|
final seedBytes = await mnemonicToSeedBytes(snp.mnemonic);
|
||||||
return BitcoinWallet(
|
return BitcoinWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
|
@ -105,10 +124,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
initialAddresses: snp.addresses,
|
initialAddresses: snp.addresses,
|
||||||
initialSilentAddresses: snp.silentAddresses,
|
initialSilentAddresses: snp.silentAddresses,
|
||||||
initialSilentAddressIndex: snp.silentAddressIndex,
|
initialSilentAddressIndex: snp.silentAddressIndex,
|
||||||
silentAddress: await SilentPaymentOwner.fromMnemonic(snp.mnemonic,
|
silentAddress: await SilentPaymentOwner.fromPrivateKeys(
|
||||||
|
scanPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
|
||||||
|
seedBytes,
|
||||||
|
network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
|
||||||
|
).derivePath(SCAN_PATH).privKey!),
|
||||||
|
spendPrivkey: ECPrivate.fromHex(bitcoin.HDWallet.fromSeed(
|
||||||
|
seedBytes,
|
||||||
|
network: snp.network == BitcoinNetwork.testnet ? bitcoin.testnet : bitcoin.bitcoin,
|
||||||
|
).derivePath(SPEND_PATH).privKey!),
|
||||||
hrp: snp.network == BitcoinNetwork.testnet ? 'tsp' : 'sp'),
|
hrp: snp.network == BitcoinNetwork.testnet ? 'tsp' : 'sp'),
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
seedBytes: seedBytes,
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
addressPageType: snp.addressPageType,
|
addressPageType: snp.addressPageType,
|
||||||
|
|
|
@ -2,12 +2,12 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_bitcoin/script_hash.dart';
|
import 'package:cw_bitcoin/script_hash.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
String jsonrpcparams(List<Object> params) {
|
String jsonrpcparams(List<Object> params) {
|
||||||
final _params = params?.map((val) => '"${val.toString()}"')?.join(',');
|
final _params = params?.map((val) => '"${val.toString()}"')?.join(',');
|
||||||
|
@ -22,10 +22,7 @@ String jsonrpc(
|
||||||
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
|
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
|
||||||
|
|
||||||
class SocketTask {
|
class SocketTask {
|
||||||
SocketTask({
|
SocketTask({required this.isSubscription, this.completer, this.subject});
|
||||||
required this.isSubscription,
|
|
||||||
this.completer,
|
|
||||||
this.subject});
|
|
||||||
|
|
||||||
final Completer<dynamic>? completer;
|
final Completer<dynamic>? completer;
|
||||||
final BehaviorSubject<dynamic>? subject;
|
final BehaviorSubject<dynamic>? subject;
|
||||||
|
@ -51,16 +48,19 @@ class ElectrumClient {
|
||||||
Timer? _aliveTimer;
|
Timer? _aliveTimer;
|
||||||
String unterminatedString;
|
String unterminatedString;
|
||||||
|
|
||||||
Future<void> connectToUri(Uri uri) async =>
|
Uri? uri;
|
||||||
|
|
||||||
|
Future<void> connectToUri(Uri uri) async {
|
||||||
|
this.uri = uri;
|
||||||
await connect(host: uri.host, port: uri.port);
|
await connect(host: uri.host, port: uri.port);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> connect({required String host, required int port}) async {
|
Future<void> connect({required String host, required int port}) async {
|
||||||
try {
|
try {
|
||||||
await socket?.close();
|
await socket?.close();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
socket = await SecureSocket.connect(host, port,
|
socket = await Socket.connect(host, port, timeout: connectionTimeout);
|
||||||
timeout: connectionTimeout, onBadCertificate: (_) => true);
|
|
||||||
_setIsConnected(true);
|
_setIsConnected(true);
|
||||||
|
|
||||||
socket!.listen((Uint8List event) {
|
socket!.listen((Uint8List event) {
|
||||||
|
@ -104,21 +104,20 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJSONStringCorrect(unterminatedString)) {
|
if (isJSONStringCorrect(unterminatedString)) {
|
||||||
final response =
|
final response = json.decode(unterminatedString) as Map<String, dynamic>;
|
||||||
json.decode(unterminatedString) as Map<String, dynamic>;
|
|
||||||
_handleResponse(response);
|
_handleResponse(response);
|
||||||
unterminatedString = '';
|
unterminatedString = '';
|
||||||
}
|
}
|
||||||
} on TypeError catch (e) {
|
} on TypeError catch (e) {
|
||||||
if (!e.toString().contains('Map<String, Object>') && !e.toString().contains('Map<String, dynamic>')) {
|
if (!e.toString().contains('Map<String, Object>') &&
|
||||||
|
!e.toString().contains('Map<String, dynamic>')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unterminatedString += message;
|
unterminatedString += message;
|
||||||
|
|
||||||
if (isJSONStringCorrect(unterminatedString)) {
|
if (isJSONStringCorrect(unterminatedString)) {
|
||||||
final response =
|
final response = json.decode(unterminatedString) as Map<String, dynamic>;
|
||||||
json.decode(unterminatedString) as Map<String, dynamic>;
|
|
||||||
_handleResponse(response);
|
_handleResponse(response);
|
||||||
// unterminatedString = null;
|
// unterminatedString = null;
|
||||||
unterminatedString = '';
|
unterminatedString = '';
|
||||||
|
@ -142,8 +141,7 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> version() =>
|
Future<List<String>> version() => call(method: 'server.version').then((dynamic result) {
|
||||||
call(method: 'server.version').then((dynamic result) {
|
|
||||||
if (result is List) {
|
if (result is List) {
|
||||||
return result.map((dynamic val) => val.toString()).toList();
|
return result.map((dynamic val) => val.toString()).toList();
|
||||||
}
|
}
|
||||||
|
@ -178,11 +176,10 @@ class ElectrumClient {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
||||||
String address, NetworkType networkType) =>
|
String address, BasedUtxoNetwork network) =>
|
||||||
call(
|
call(
|
||||||
method: 'blockchain.scripthash.listunspent',
|
method: 'blockchain.scripthash.listunspent',
|
||||||
params: [scriptHash(address, networkType: networkType)])
|
params: [scriptHash(address, network: network)]).then((dynamic result) {
|
||||||
.then((dynamic result) {
|
|
||||||
if (result is List) {
|
if (result is List) {
|
||||||
return result.map((dynamic val) {
|
return result.map((dynamic val) {
|
||||||
if (val is Map<String, dynamic>) {
|
if (val is Map<String, dynamic>) {
|
||||||
|
@ -229,8 +226,7 @@ class ElectrumClient {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getTransactionRaw(
|
Future<Map<String, dynamic>> getTransactionRaw({required String hash}) async =>
|
||||||
{required String hash}) async =>
|
|
||||||
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000)
|
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000)
|
||||||
.then((dynamic result) {
|
.then((dynamic result) {
|
||||||
if (result is Map<String, dynamic>) {
|
if (result is Map<String, dynamic>) {
|
||||||
|
@ -240,8 +236,7 @@ class ElectrumClient {
|
||||||
return <String, dynamic>{};
|
return <String, dynamic>{};
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<String> getTransactionHex(
|
Future<String> getTransactionHex({required String hash}) async =>
|
||||||
{required String hash}) async =>
|
|
||||||
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000)
|
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000)
|
||||||
.then((dynamic result) {
|
.then((dynamic result) {
|
||||||
if (result is String) {
|
if (result is String) {
|
||||||
|
@ -252,29 +247,43 @@ class ElectrumClient {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<String> broadcastTransaction(
|
Future<String> broadcastTransaction(
|
||||||
{required String transactionRaw}) async =>
|
{required String transactionRaw, BasedUtxoNetwork? network}) async {
|
||||||
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
if (network == BitcoinNetwork.testnet) {
|
||||||
.then((dynamic result) {
|
return http
|
||||||
if (result is String) {
|
.post(Uri(scheme: 'https', host: 'blockstream.info', path: '/testnet/api/tx'),
|
||||||
return result;
|
headers: <String, String>{'Content-Type': 'application/json; charset=utf-8'},
|
||||||
|
body: transactionRaw)
|
||||||
|
.then((http.Response response) {
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return response.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
throw Exception('Failed to broadcast transaction: ${response.body}');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getMerkle(
|
return call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
||||||
{required String hash, required int height}) async =>
|
.then((dynamic result) {
|
||||||
await call(
|
if (result is String) {
|
||||||
method: 'blockchain.transaction.get_merkle',
|
return result;
|
||||||
params: [hash, height]) as Map<String, dynamic>;
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getHeader({required int height}) async =>
|
return '';
|
||||||
await call(method: 'blockchain.block.get_header', params: [height])
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getMerkle({required String hash, required int height}) async =>
|
||||||
|
await call(method: 'blockchain.transaction.get_merkle', params: [hash, height])
|
||||||
as Map<String, dynamic>;
|
as Map<String, dynamic>;
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getHeader({required int height}) async =>
|
||||||
|
await call(method: 'blockchain.block.get_header', params: [height]) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getTweaks({required int height}) async =>
|
||||||
|
await call(method: 'blockchain.block.tweaks', params: [height]) as Map<String, dynamic>;
|
||||||
|
|
||||||
Future<double> estimatefee({required int p}) =>
|
Future<double> estimatefee({required int p}) =>
|
||||||
call(method: 'blockchain.estimatefee', params: [p])
|
call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) {
|
||||||
.then((dynamic result) {
|
|
||||||
if (result is double) {
|
if (result is double) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -314,20 +323,17 @@ class ElectrumClient {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<List<int>> feeRates() async {
|
Future<List<int>> feeRates({BasedUtxoNetwork? network}) async {
|
||||||
|
if (network == BitcoinNetwork.testnet) {
|
||||||
|
return [1, 1, 1];
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
final topDoubleString = await estimatefee(p: 1);
|
final topDoubleString = await estimatefee(p: 1);
|
||||||
final middleDoubleString = await estimatefee(p: 5);
|
final middleDoubleString = await estimatefee(p: 5);
|
||||||
final bottomDoubleString = await estimatefee(p: 100);
|
final bottomDoubleString = await estimatefee(p: 100);
|
||||||
final top =
|
final top = (stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000).round();
|
||||||
(stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000)
|
final middle = (stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000).round();
|
||||||
.round();
|
final bottom = (stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000).round();
|
||||||
final middle =
|
|
||||||
(stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000)
|
|
||||||
.round();
|
|
||||||
final bottom =
|
|
||||||
(stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000)
|
|
||||||
.round();
|
|
||||||
|
|
||||||
return [bottom, middle, top];
|
return [bottom, middle, top];
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
@ -365,16 +371,14 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
BehaviorSubject<T>? subscribe<T>(
|
BehaviorSubject<T>? subscribe<T>(
|
||||||
{required String id,
|
{required String id, required String method, List<Object> params = const []}) {
|
||||||
required String method,
|
|
||||||
List<Object> params = const []}) {
|
|
||||||
try {
|
try {
|
||||||
final subscription = BehaviorSubject<T>();
|
final subscription = BehaviorSubject<T>();
|
||||||
_regisrySubscription(id, subscription);
|
_regisrySubscription(id, subscription);
|
||||||
socket!.write(jsonrpc(method: method, id: _id, params: params));
|
socket!.write(jsonrpc(method: method, id: _id, params: params));
|
||||||
|
|
||||||
return subscription;
|
return subscription;
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -391,9 +395,7 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> callWithTimeout(
|
Future<dynamic> callWithTimeout(
|
||||||
{required String method,
|
{required String method, List<Object> params = const [], int timeout = 4000}) async {
|
||||||
List<Object> params = const [],
|
|
||||||
int timeout = 4000}) async {
|
|
||||||
try {
|
try {
|
||||||
final completer = Completer<dynamic>();
|
final completer = Completer<dynamic>();
|
||||||
_id += 1;
|
_id += 1;
|
||||||
|
@ -407,7 +409,7 @@ class ElectrumClient {
|
||||||
});
|
});
|
||||||
|
|
||||||
return completer.future;
|
return completer.future;
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,8 +420,8 @@ class ElectrumClient {
|
||||||
onConnectionStatusChange = null;
|
onConnectionStatusChange = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registryTask(int id, Completer<dynamic> completer) => _tasks[id.toString()] =
|
void _registryTask(int id, Completer<dynamic> completer) =>
|
||||||
SocketTask(completer: completer, isSubscription: false);
|
_tasks[id.toString()] = SocketTask(completer: completer, isSubscription: false);
|
||||||
|
|
||||||
void _regisrySubscription(String id, BehaviorSubject<dynamic> subject) =>
|
void _regisrySubscription(String id, BehaviorSubject<dynamic> subject) =>
|
||||||
_tasks[id] = SocketTask(subject: subject, isSubscription: true);
|
_tasks[id] = SocketTask(subject: subject, isSubscription: true);
|
||||||
|
@ -440,8 +442,7 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _methodHandler(
|
void _methodHandler({required String method, required Map<String, dynamic> request}) {
|
||||||
{required String method, required Map<String, dynamic> request}) {
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case 'blockchain.scripthash.subscribe':
|
case 'blockchain.scripthash.subscribe':
|
||||||
final params = request['params'] as List<dynamic>;
|
final params = request['params'] as List<dynamic>;
|
||||||
|
@ -472,8 +473,8 @@ class ElectrumClient {
|
||||||
_methodHandler(method: method, request: response);
|
_methodHandler(method: method, request: response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id != null){
|
if (id != null) {
|
||||||
_finish(id, result);
|
_finish(id, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:isolate';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cw_bitcoin/address_to_output_script.dart';
|
import 'package:cw_bitcoin/address_to_output_script.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
|
@ -133,7 +134,7 @@ abstract class ElectrumWalletBase
|
||||||
Map<String, BehaviorSubject<Object>?> _scripthashesUpdateSubject;
|
Map<String, BehaviorSubject<Object>?> _scripthashesUpdateSubject;
|
||||||
BehaviorSubject<Object>? _chainTipUpdateSubject;
|
BehaviorSubject<Object>? _chainTipUpdateSubject;
|
||||||
bool _isTransactionUpdating;
|
bool _isTransactionUpdating;
|
||||||
// Future<Isolate>? _isolate;
|
Future<Isolate>? _isolate;
|
||||||
|
|
||||||
void Function(FlutterErrorDetails)? _onError;
|
void Function(FlutterErrorDetails)? _onError;
|
||||||
Timer? _autoSaveTimer;
|
Timer? _autoSaveTimer;
|
||||||
|
@ -147,66 +148,66 @@ abstract class ElectrumWalletBase
|
||||||
Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
|
Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
|
||||||
}
|
}
|
||||||
|
|
||||||
// @action
|
@action
|
||||||
// Future<void> _setListeners(int height, {int? chainTip}) async {
|
Future<void> _setListeners(int height, {int? chainTip}) async {
|
||||||
// final currentChainTip = chainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0;
|
final currentChainTip = chainTip ?? await electrumClient.getCurrentBlockChainTip() ?? 0;
|
||||||
// syncStatus = AttemptingSyncStatus();
|
syncStatus = AttemptingSyncStatus();
|
||||||
|
|
||||||
// if (_isolate != null) {
|
if (_isolate != null) {
|
||||||
// final runningIsolate = await _isolate!;
|
final runningIsolate = await _isolate!;
|
||||||
// runningIsolate.kill(priority: Isolate.immediate);
|
runningIsolate.kill(priority: Isolate.immediate);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// final receivePort = ReceivePort();
|
final receivePort = ReceivePort();
|
||||||
// _isolate = Isolate.spawn(
|
_isolate = Isolate.spawn(
|
||||||
// startRefresh,
|
startRefresh,
|
||||||
// ScanData(
|
ScanData(
|
||||||
// sendPort: receivePort.sendPort,
|
sendPort: receivePort.sendPort,
|
||||||
// primarySilentAddress: walletAddresses.primarySilentAddress!,
|
primarySilentAddress: walletAddresses.primarySilentAddress!,
|
||||||
// networkType: networkType,
|
network: network,
|
||||||
// height: height,
|
height: height,
|
||||||
// chainTip: currentChainTip,
|
chainTip: currentChainTip,
|
||||||
// electrumClient: ElectrumClient(),
|
electrumClient: ElectrumClient(),
|
||||||
// transactionHistoryIds: transactionHistory.transactions.keys.toList(),
|
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
|
||||||
// node: electrumClient.uri.toString(),
|
node: electrumClient.uri.toString(),
|
||||||
// labels: walletAddresses.labels,
|
labels: walletAddresses.labels,
|
||||||
// ));
|
));
|
||||||
|
|
||||||
// await for (var message in receivePort) {
|
await for (var message in receivePort) {
|
||||||
// if (message is BitcoinUnspent) {
|
if (message is BitcoinUnspent) {
|
||||||
// if (!unspentCoins.any((utx) =>
|
if (!unspentCoins.any((utx) =>
|
||||||
// utx.hash.contains(message.hash) &&
|
utx.hash.contains(message.hash) &&
|
||||||
// utx.vout == message.vout &&
|
utx.vout == message.vout &&
|
||||||
// utx.address.contains(message.address))) {
|
utx.address.contains(message.address))) {
|
||||||
// unspentCoins.add(message);
|
unspentCoins.add(message);
|
||||||
|
|
||||||
// if (unspentCoinsInfo.values.any((element) =>
|
if (unspentCoinsInfo.values.any((element) =>
|
||||||
// element.walletId.contains(id) &&
|
element.walletId.contains(id) &&
|
||||||
// element.hash.contains(message.hash) &&
|
element.hash.contains(message.hash) &&
|
||||||
// element.address.contains(message.address))) {
|
element.address.contains(message.address))) {
|
||||||
// _addCoinInfo(message);
|
_addCoinInfo(message);
|
||||||
|
|
||||||
// await walletInfo.save();
|
await walletInfo.save();
|
||||||
// await save();
|
await save();
|
||||||
// }
|
}
|
||||||
|
|
||||||
// balance[currency] = await _fetchBalances();
|
balance[currency] = await _fetchBalances();
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (message is Map<String, ElectrumTransactionInfo>) {
|
if (message is Map<String, ElectrumTransactionInfo>) {
|
||||||
// transactionHistory.addMany(message);
|
transactionHistory.addMany(message);
|
||||||
// await transactionHistory.save();
|
await transactionHistory.save();
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // check if is a SyncStatus type since "is SyncStatus" doesn't work here
|
// check if is a SyncStatus type since "is SyncStatus" doesn't work here
|
||||||
// if (message is SyncResponse) {
|
if (message is SyncResponse) {
|
||||||
// syncStatus = message.syncStatus;
|
syncStatus = message.syncStatus;
|
||||||
// walletInfo.restoreHeight = message.height;
|
walletInfo.restoreHeight = message.height;
|
||||||
// await walletInfo.save();
|
await walletInfo.save();
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@override
|
@override
|
||||||
|
@ -248,11 +249,11 @@ abstract class ElectrumWalletBase
|
||||||
};
|
};
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
|
||||||
// final currentChainTip = await electrumClient.getCurrentBlockChainTip();
|
final currentChainTip = await electrumClient.getCurrentBlockChainTip();
|
||||||
|
|
||||||
// if ((currentChainTip ?? 0) > walletInfo.restoreHeight) {
|
if ((currentChainTip ?? 0) > walletInfo.restoreHeight) {
|
||||||
// _setListeners(walletInfo.restoreHeight, chainTip: currentChainTip);
|
_setListeners(walletInfo.restoreHeight, chainTip: currentChainTip);
|
||||||
// }
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
syncStatus = FailedSyncStatus();
|
syncStatus = FailedSyncStatus();
|
||||||
|
@ -643,7 +644,7 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rescan({required int height, int? chainTip, ScanData? scanData}) async {
|
Future<void> rescan({required int height, int? chainTip, ScanData? scanData}) async {
|
||||||
// _setListeners(height);
|
_setListeners(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -908,7 +909,7 @@ abstract class ElectrumWalletBase
|
||||||
try {
|
try {
|
||||||
final currentHeight = await electrumClient.getCurrentBlockChainTip();
|
final currentHeight = await electrumClient.getCurrentBlockChainTip();
|
||||||
if (currentHeight != null) walletInfo.restoreHeight = currentHeight;
|
if (currentHeight != null) walletInfo.restoreHeight = currentHeight;
|
||||||
// _setListeners(walletInfo.restoreHeight, chainTip: currentHeight);
|
_setListeners(walletInfo.restoreHeight, chainTip: currentHeight);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
_onError?.call(FlutterErrorDetails(
|
_onError?.call(FlutterErrorDetails(
|
||||||
|
@ -1011,10 +1012,10 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
class ScanData {
|
class ScanData {
|
||||||
final SendPort sendPort;
|
final SendPort sendPort;
|
||||||
final SilentPaymentReceiver primarySilentAddress;
|
final SilentPaymentOwner primarySilentAddress;
|
||||||
final int height;
|
final int height;
|
||||||
final String node;
|
final String node;
|
||||||
final bitcoin.NetworkType networkType;
|
final BasedUtxoNetwork network;
|
||||||
final int chainTip;
|
final int chainTip;
|
||||||
final ElectrumClient electrumClient;
|
final ElectrumClient electrumClient;
|
||||||
final List<String> transactionHistoryIds;
|
final List<String> transactionHistoryIds;
|
||||||
|
@ -1025,7 +1026,7 @@ class ScanData {
|
||||||
required this.primarySilentAddress,
|
required this.primarySilentAddress,
|
||||||
required this.height,
|
required this.height,
|
||||||
required this.node,
|
required this.node,
|
||||||
required this.networkType,
|
required this.network,
|
||||||
required this.chainTip,
|
required this.chainTip,
|
||||||
required this.electrumClient,
|
required this.electrumClient,
|
||||||
required this.transactionHistoryIds,
|
required this.transactionHistoryIds,
|
||||||
|
@ -1038,7 +1039,7 @@ class ScanData {
|
||||||
primarySilentAddress: scanData.primarySilentAddress,
|
primarySilentAddress: scanData.primarySilentAddress,
|
||||||
height: newHeight,
|
height: newHeight,
|
||||||
node: scanData.node,
|
node: scanData.node,
|
||||||
networkType: scanData.networkType,
|
network: scanData.network,
|
||||||
chainTip: scanData.chainTip,
|
chainTip: scanData.chainTip,
|
||||||
transactionHistoryIds: scanData.transactionHistoryIds,
|
transactionHistoryIds: scanData.transactionHistoryIds,
|
||||||
electrumClient: scanData.electrumClient,
|
electrumClient: scanData.electrumClient,
|
||||||
|
@ -1054,333 +1055,220 @@ class SyncResponse {
|
||||||
SyncResponse(this.height, this.syncStatus);
|
SyncResponse(this.height, this.syncStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> startRefresh(ScanData scanData) async {
|
Future<void> startRefresh(ScanData scanData) async {
|
||||||
// var cachedBlockchainHeight = scanData.chainTip;
|
var cachedBlockchainHeight = scanData.chainTip;
|
||||||
|
|
||||||
// Future<int> getNodeHeightOrUpdate(int baseHeight) async {
|
Future<int> getNodeHeightOrUpdate(int baseHeight) async {
|
||||||
// if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) {
|
if (cachedBlockchainHeight < baseHeight || cachedBlockchainHeight == 0) {
|
||||||
// final electrumClient = scanData.electrumClient;
|
final electrumClient = scanData.electrumClient;
|
||||||
// if (!electrumClient.isConnected) {
|
if (!electrumClient.isConnected) {
|
||||||
// final node = scanData.node;
|
final node = scanData.node;
|
||||||
// await electrumClient.connectToUri(Uri.parse(node));
|
await electrumClient.connectToUri(Uri.parse(node));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// cachedBlockchainHeight =
|
cachedBlockchainHeight =
|
||||||
// await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight;
|
await electrumClient.getCurrentBlockChainTip() ?? cachedBlockchainHeight;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return cachedBlockchainHeight;
|
return cachedBlockchainHeight;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// var lastKnownBlockHeight = 0;
|
var lastKnownBlockHeight = 0;
|
||||||
// var initialSyncHeight = 0;
|
var initialSyncHeight = 0;
|
||||||
|
|
||||||
// var syncHeight = scanData.height;
|
var syncHeight = scanData.height;
|
||||||
// var currentChainTip = scanData.chainTip;
|
var currentChainTip = scanData.chainTip;
|
||||||
|
|
||||||
// if (syncHeight <= 0) {
|
if (syncHeight <= 0) {
|
||||||
// syncHeight = currentChainTip;
|
syncHeight = currentChainTip;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (initialSyncHeight <= 0) {
|
if (initialSyncHeight <= 0) {
|
||||||
// initialSyncHeight = syncHeight;
|
initialSyncHeight = syncHeight;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (lastKnownBlockHeight == syncHeight) {
|
if (lastKnownBlockHeight == syncHeight) {
|
||||||
// scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus()));
|
scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus()));
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Run this until no more blocks left to scan txs. At first this was recursive
|
// Run this until no more blocks left to scan txs. At first this was recursive
|
||||||
// // i.e. re-calling the startRefresh function but this was easier for the above values to retain
|
// i.e. re-calling the startRefresh function but this was easier for the above values to retain
|
||||||
// // their initial values
|
// their initial values
|
||||||
// while (true) {
|
while (true) {
|
||||||
// lastKnownBlockHeight = syncHeight;
|
lastKnownBlockHeight = syncHeight;
|
||||||
|
|
||||||
// final syncingStatus =
|
final syncingStatus =
|
||||||
// SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight);
|
SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight);
|
||||||
// scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
|
scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
|
||||||
|
|
||||||
// if (syncingStatus.blocksLeft <= 0) {
|
if (syncingStatus.blocksLeft <= 0) {
|
||||||
// scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus()));
|
scanData.sendPort.send(SyncResponse(currentChainTip, SyncedSyncStatus()));
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // print(["Scanning from height:", syncHeight]);
|
print(["Scanning from height:", syncHeight]);
|
||||||
|
|
||||||
// try {
|
try {
|
||||||
// final networkPath =
|
// Get all the tweaks from the block
|
||||||
// scanData.networkType.network == bitcoin.BtcNetwork.mainnet ? "" : "/testnet";
|
final electrumClient = scanData.electrumClient;
|
||||||
|
if (!electrumClient.isConnected) {
|
||||||
|
final node = scanData.node;
|
||||||
|
await electrumClient.connectToUri(Uri.parse(node));
|
||||||
|
}
|
||||||
|
final tweaks = await electrumClient.getTweaks(height: syncHeight);
|
||||||
|
|
||||||
// // This endpoint gets up to 10 latest blocks from the given height
|
for (var i = 0; i < tweaks.length; i++) {
|
||||||
// final tenNewestBlocks =
|
try {
|
||||||
// (await http.get(Uri.parse("https://blockstream.info$networkPath/api/blocks/$syncHeight")))
|
// final txid = tweaks.keys.toList()[i];
|
||||||
// .body;
|
final details = tweaks.values.toList()[i];
|
||||||
// var decodedBlocks = json.decode(tenNewestBlocks) as List<dynamic>;
|
print(["details", details]);
|
||||||
|
final output_pubkeys = (details["output_pubkeys"] as List<String>);
|
||||||
|
|
||||||
// decodedBlocks.sort((a, b) => (a["height"] as int).compareTo(b["height"] as int));
|
// print(["Scanning tx:", txid]);
|
||||||
// decodedBlocks =
|
|
||||||
// decodedBlocks.where((element) => (element["height"] as int) >= syncHeight).toList();
|
|
||||||
|
|
||||||
// // for each block, get up to 25 txs
|
// TODO: if tx already scanned & stored skip
|
||||||
// for (var i = 0; i < decodedBlocks.length; i++) {
|
// if (scanData.transactionHistoryIds.contains(txid)) {
|
||||||
// final blockJson = decodedBlocks[i];
|
// // already scanned tx, continue to next tx
|
||||||
// final blockHash = blockJson["id"];
|
// pos++;
|
||||||
// final txCount = blockJson["tx_count"] as int;
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
// // print(["Scanning block index:", i, "with tx count:", txCount]);
|
final result = SilentPayment.scanTweak(
|
||||||
|
scanData.primarySilentAddress.b_scan,
|
||||||
|
scanData.primarySilentAddress.B_spend,
|
||||||
|
details["tweak"] as String,
|
||||||
|
output_pubkeys.map((e) => BytesUtils.fromHexString(e)).toList(),
|
||||||
|
labels: scanData.labels,
|
||||||
|
);
|
||||||
|
|
||||||
// int startIndex = 0;
|
if (result.isEmpty) {
|
||||||
// // go through each tx in block until no more txs are left
|
// no results tx, continue to next tx
|
||||||
// while (startIndex < txCount) {
|
continue;
|
||||||
// // This endpoint gets up to 25 txs from the given block hash and start index
|
}
|
||||||
// final twentyFiveTxs = json.decode((await http.get(Uri.parse(
|
|
||||||
// "https://blockstream.info$networkPath/api/block/$blockHash/txs/$startIndex")))
|
|
||||||
// .body) as List<dynamic>;
|
|
||||||
|
|
||||||
// // print(["Scanning txs index:", startIndex]);
|
if (result.length > 1) {
|
||||||
|
print("MULTIPLE UNSPENT COINS FOUND!");
|
||||||
|
} else {
|
||||||
|
print("UNSPENT COIN FOUND!");
|
||||||
|
}
|
||||||
|
|
||||||
// // For each tx, apply silent payment filtering and do shared secret calculation when applied
|
// result.forEach((key, value) async {
|
||||||
// for (var i = 0; i < twentyFiveTxs.length; i++) {
|
// final outpoint = output_pubkeys[key];
|
||||||
// try {
|
|
||||||
// final tx = twentyFiveTxs[i];
|
|
||||||
// final txid = tx["txid"] as String;
|
|
||||||
|
|
||||||
// // print(["Scanning tx:", txid]);
|
// if (outpoint == null) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
// // TODO: if tx already scanned & stored skip
|
// final tweak = value[0];
|
||||||
// // if (scanData.transactionHistoryIds.contains(txid)) {
|
// String? label;
|
||||||
// // // already scanned tx, continue to next tx
|
// if (value.length > 1) label = value[1];
|
||||||
// // pos++;
|
|
||||||
// // continue;
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// List<String> pubkeys = [];
|
// final txInfo = ElectrumTransactionInfo(
|
||||||
// List<bitcoin.Outpoint> outpoints = [];
|
// WalletType.bitcoin,
|
||||||
|
// id: txid,
|
||||||
|
// height: syncHeight,
|
||||||
|
// amount: outpoint.value!,
|
||||||
|
// fee: 0,
|
||||||
|
// direction: TransactionDirection.incoming,
|
||||||
|
// isPending: false,
|
||||||
|
// date: DateTime.fromMillisecondsSinceEpoch((blockJson["timestamp"] as int) * 1000),
|
||||||
|
// confirmations: currentChainTip - syncHeight,
|
||||||
|
// to: bitcoin.SilentPaymentAddress.createLabeledSilentPaymentAddress(
|
||||||
|
// scanData.primarySilentAddress.scanPubkey,
|
||||||
|
// scanData.primarySilentAddress.spendPubkey,
|
||||||
|
// label != null ? label.fromHex : "0".fromHex,
|
||||||
|
// hrp: scanData.primarySilentAddress.hrp,
|
||||||
|
// version: scanData.primarySilentAddress.version)
|
||||||
|
// .toString(),
|
||||||
|
// unspent: null,
|
||||||
|
// );
|
||||||
|
|
||||||
// bool skip = false;
|
// final status = json.decode((await http
|
||||||
|
// .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends")))
|
||||||
|
// .body) as List<dynamic>;
|
||||||
|
|
||||||
// for (var i = 0; i < (tx["vin"] as List<dynamic>).length; i++) {
|
// bool spent = false;
|
||||||
// final input = tx["vin"][i];
|
// for (final s in status) {
|
||||||
// final prevout = input["prevout"];
|
// if ((s["spent"] as bool) == true) {
|
||||||
// final scriptPubkeyType = prevout["scriptpubkey_type"];
|
// spent = true;
|
||||||
// String? pubkey;
|
|
||||||
|
|
||||||
// if (scriptPubkeyType == "v0_p2wpkh" || scriptPubkeyType == "v1_p2tr") {
|
// scanData.sendPort.send({txid: txInfo});
|
||||||
// final witness = input["witness"];
|
|
||||||
// if (witness == null) {
|
|
||||||
// skip = true;
|
|
||||||
// // print("Skipping, no witness");
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (witness.length == 2) {
|
// final sentTxId = s["txid"] as String;
|
||||||
// pubkey = witness[1] as String;
|
// final sentTx = json.decode(
|
||||||
// } else if (witness.length == 1) {
|
// (await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId")))
|
||||||
// pubkey = "02" + (prevout["scriptpubkey"] as String).fromHex.sublist(2).hex;
|
// .body);
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (scriptPubkeyType == "p2pkh") {
|
// int amount = 0;
|
||||||
// pubkey = bitcoin.P2pkhAddress(
|
// for (final out in (sentTx["vout"] as List<dynamic>)) {
|
||||||
// scriptSig: bitcoin.Script.fromRaw(hexData: input["scriptsig"] as String))
|
// amount += out["value"] as int;
|
||||||
// .pubkey;
|
// }
|
||||||
// }
|
|
||||||
|
|
||||||
// if (pubkey == null) {
|
// final height = s["status"]["block_height"] as int;
|
||||||
// skip = true;
|
|
||||||
// // print("Skipping, invalid witness");
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pubkeys.add(pubkey);
|
// scanData.sendPort.send({
|
||||||
// outpoints.add(
|
// sentTxId: ElectrumTransactionInfo(
|
||||||
// bitcoin.Outpoint(txid: input["txid"] as String, index: input["vout"] as int));
|
// 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,
|
||||||
|
// )
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// if (skip) {
|
// if (spent) {
|
||||||
// // skipped tx, continue to next tx
|
// return;
|
||||||
// continue;
|
// }
|
||||||
// }
|
|
||||||
|
|
||||||
// Map<String, bitcoin.Outpoint> outpointsByP2TRpubkey = {};
|
// final unspent = BitcoinUnspent(
|
||||||
// for (var i = 0; i < (tx["vout"] as List<dynamic>).length; i++) {
|
// BitcoinAddressRecord(
|
||||||
// final output = tx["vout"][i];
|
// bitcoin.P2trAddress(program: key, networkType: scanData.network).address,
|
||||||
// if (output["scriptpubkey_type"] != "v1_p2tr") {
|
// index: 0,
|
||||||
// // print("Skipping, not a v1_p2tr output");
|
// isHidden: true,
|
||||||
// continue;
|
// isUsed: true,
|
||||||
// }
|
// silentAddressLabel: null,
|
||||||
|
// silentPaymentTweak: tweak,
|
||||||
|
// type: bitcoin.AddressType.p2tr,
|
||||||
|
// ),
|
||||||
|
// txid,
|
||||||
|
// outpoint.value!,
|
||||||
|
// outpoint.index,
|
||||||
|
// silentPaymentTweak: tweak,
|
||||||
|
// type: bitcoin.AddressType.p2tr,
|
||||||
|
// );
|
||||||
|
|
||||||
// final script = (output["scriptpubkey"] as String).fromHex;
|
// // found utxo for tx, send unspent coin to main isolate
|
||||||
|
// scanData.sendPort.send(unspent);
|
||||||
|
|
||||||
// // final alreadySpentOutput = (await electrumClient.getHistory(
|
// // also send tx data for tx history
|
||||||
// // scriptHashFromScript(script, networkType: scanData.networkType)))
|
// txInfo.unspent = unspent;
|
||||||
// // .length >
|
// scanData.sendPort.send({txid: txInfo});
|
||||||
// // 1;
|
// });
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
// // if (alreadySpentOutput) {
|
// Finished scanning block, add 1 to height and continue to next block in loop
|
||||||
// // print("Skipping, invalid witness");
|
syncHeight += 1;
|
||||||
// // break;
|
currentChainTip = await getNodeHeightOrUpdate(syncHeight);
|
||||||
// // }
|
scanData.sendPort.send(SyncResponse(syncHeight,
|
||||||
|
SyncingSyncStatus.fromHeightValues(currentChainTip, initialSyncHeight, syncHeight)));
|
||||||
|
} catch (e, stacktrace) {
|
||||||
|
print(stacktrace);
|
||||||
|
print(e.toString());
|
||||||
|
|
||||||
// final p2tr = bitcoin.P2trAddress(
|
scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus()));
|
||||||
// program: script.sublist(2).hex, networkType: scanData.networkType);
|
break;
|
||||||
// final address = p2tr.address;
|
}
|
||||||
|
}
|
||||||
// print(["Verifying taproot address:", address]);
|
}
|
||||||
|
|
||||||
// outpointsByP2TRpubkey[script.sublist(2).hex] =
|
|
||||||
// bitcoin.Outpoint(txid: txid, index: i, value: output["value"] as int);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (pubkeys.isEmpty || outpoints.isEmpty || outpointsByP2TRpubkey.isEmpty) {
|
|
||||||
// // skipped tx, continue to next tx
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final outpointHash = bitcoin.SilentPayment.hashOutpoints(outpoints);
|
|
||||||
|
|
||||||
// final result = bitcoin.scanOutputs(
|
|
||||||
// scanData.primarySilentAddress.scanPrivkey,
|
|
||||||
// scanData.primarySilentAddress.spendPubkey,
|
|
||||||
// bitcoin.getSumInputPubKeys(pubkeys),
|
|
||||||
// outpointHash,
|
|
||||||
// outpointsByP2TRpubkey.keys.map((e) => e.fromHex).toList(),
|
|
||||||
// labels: scanData.labels,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (result.isEmpty) {
|
|
||||||
// // no results tx, continue to next tx
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (result.length > 1) {
|
|
||||||
// print("MULTIPLE UNSPENT COINS FOUND!");
|
|
||||||
// } else {
|
|
||||||
// print("UNSPENT COIN FOUND!");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// result.forEach((key, value) async {
|
|
||||||
// final outpoint = outpointsByP2TRpubkey[key];
|
|
||||||
|
|
||||||
// if (outpoint == null) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final tweak = value[0];
|
|
||||||
// String? label;
|
|
||||||
// if (value.length > 1) label = value[1];
|
|
||||||
|
|
||||||
// final txInfo = ElectrumTransactionInfo(
|
|
||||||
// WalletType.bitcoin,
|
|
||||||
// id: txid,
|
|
||||||
// height: syncHeight,
|
|
||||||
// amount: outpoint.value!,
|
|
||||||
// fee: 0,
|
|
||||||
// direction: TransactionDirection.incoming,
|
|
||||||
// isPending: false,
|
|
||||||
// date: DateTime.fromMillisecondsSinceEpoch((blockJson["timestamp"] as int) * 1000),
|
|
||||||
// confirmations: currentChainTip - syncHeight,
|
|
||||||
// to: bitcoin.SilentPaymentAddress.createLabeledSilentPaymentAddress(
|
|
||||||
// scanData.primarySilentAddress.scanPubkey,
|
|
||||||
// scanData.primarySilentAddress.spendPubkey,
|
|
||||||
// label != null ? label.fromHex : "0".fromHex,
|
|
||||||
// hrp: scanData.primarySilentAddress.hrp,
|
|
||||||
// version: scanData.primarySilentAddress.version)
|
|
||||||
// .toString(),
|
|
||||||
// unspent: null,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// final status = json.decode((await http
|
|
||||||
// .get(Uri.parse("https://blockstream.info/testnet/api/tx/$txid/outspends")))
|
|
||||||
// .body) as List<dynamic>;
|
|
||||||
|
|
||||||
// bool spent = false;
|
|
||||||
// for (final s in status) {
|
|
||||||
// if ((s["spent"] as bool) == true) {
|
|
||||||
// spent = true;
|
|
||||||
|
|
||||||
// scanData.sendPort.send({txid: txInfo});
|
|
||||||
|
|
||||||
// final sentTxId = s["txid"] as String;
|
|
||||||
// final sentTx = json.decode((await http
|
|
||||||
// .get(Uri.parse("https://blockstream.info/testnet/api/tx/$sentTxId")))
|
|
||||||
// .body);
|
|
||||||
|
|
||||||
// int amount = 0;
|
|
||||||
// for (final out in (sentTx["vout"] as List<dynamic>)) {
|
|
||||||
// amount += out["value"] as int;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final height = s["status"]["block_height"] as int;
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
// )
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (spent) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// final unspent = BitcoinUnspent(
|
|
||||||
// BitcoinAddressRecord(
|
|
||||||
// bitcoin.P2trAddress(program: key, networkType: scanData.networkType).address,
|
|
||||||
// index: 0,
|
|
||||||
// isHidden: true,
|
|
||||||
// isUsed: true,
|
|
||||||
// silentAddressLabel: null,
|
|
||||||
// silentPaymentTweak: tweak,
|
|
||||||
// type: bitcoin.AddressType.p2tr,
|
|
||||||
// ),
|
|
||||||
// txid,
|
|
||||||
// outpoint.value!,
|
|
||||||
// outpoint.index,
|
|
||||||
// silentPaymentTweak: tweak,
|
|
||||||
// type: bitcoin.AddressType.p2tr,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // found utxo for tx, send unspent coin to main isolate
|
|
||||||
// scanData.sendPort.send(unspent);
|
|
||||||
|
|
||||||
// // also send tx data for tx history
|
|
||||||
// txInfo.unspent = unspent;
|
|
||||||
// scanData.sendPort.send({txid: txInfo});
|
|
||||||
// });
|
|
||||||
// } catch (_) {}
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Finished scanning batch of txs in block, add 25 to start index and continue to next block in loop
|
|
||||||
// startIndex += 25;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 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());
|
|
||||||
|
|
||||||
// scanData.sendPort.send(SyncResponse(syncHeight, NotConnectedSyncStatus()));
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
class EstimatedTxResult {
|
class EstimatedTxResult {
|
||||||
EstimatedTxResult(
|
EstimatedTxResult(
|
||||||
|
|
|
@ -216,7 +216,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
for (int i = 0; i < silentAddresses.length; i++) {
|
for (int i = 0; i < silentAddresses.length; i++) {
|
||||||
final silentAddressRecord = silentAddresses[i];
|
final silentAddressRecord = silentAddresses[i];
|
||||||
final silentAddress =
|
final silentAddress =
|
||||||
SilentPaymentDestination.fromAddress(silentAddressRecord.address, 0).spendPubkey.toHex();
|
SilentPaymentDestination.fromAddress(silentAddressRecord.address, 0).B_spend.toHex();
|
||||||
|
|
||||||
if (silentAddressRecord.silentPaymentTweak != null)
|
if (silentAddressRecord.silentPaymentTweak != null)
|
||||||
labels[silentAddress] = silentAddressRecord.silentPaymentTweak!;
|
labels[silentAddress] = silentAddressRecord.silentPaymentTweak!;
|
||||||
|
@ -232,7 +232,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
final address = BitcoinAddressRecord(
|
final address = BitcoinAddressRecord(
|
||||||
SilentPaymentAddress.createLabeledSilentPaymentAddress(
|
SilentPaymentAddress.createLabeledSilentPaymentAddress(
|
||||||
primarySilentAddress!.scanPubkey, primarySilentAddress!.spendPubkey, tweak,
|
primarySilentAddress!.B_scan, primarySilentAddress!.B_spend, tweak,
|
||||||
hrp: primarySilentAddress!.hrp, version: primarySilentAddress!.version)
|
hrp: primarySilentAddress!.hrp, version: primarySilentAddress!.version)
|
||||||
.toString(),
|
.toString(),
|
||||||
index: currentSilentAddressIndex,
|
index: currentSilentAddressIndex,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
@ -20,17 +21,18 @@ part 'litecoin_wallet.g.dart';
|
||||||
class LitecoinWallet = LitecoinWalletBase with _$LitecoinWallet;
|
class LitecoinWallet = LitecoinWalletBase with _$LitecoinWallet;
|
||||||
|
|
||||||
abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
LitecoinWalletBase(
|
LitecoinWalletBase({
|
||||||
{required String mnemonic,
|
required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required Uint8List seedBytes,
|
required Uint8List seedBytes,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
String? addressPageType,
|
||||||
ElectrumBalance? initialBalance,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
ElectrumBalance? initialBalance,
|
||||||
int initialChangeAddressIndex = 0})
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
: super(
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
|
}) : super(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -41,41 +43,41 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
currency: CryptoCurrency.ltc) {
|
currency: CryptoCurrency.ltc) {
|
||||||
walletAddresses = LitecoinWalletAddresses(
|
walletAddresses = LitecoinWalletAddresses(
|
||||||
walletInfo,
|
walletInfo,
|
||||||
electrumClient: electrumClient,
|
initialAddresses: initialAddresses,
|
||||||
initialAddresses: initialAddresses,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
mainHd: hd,
|
||||||
mainHd: hd,
|
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
||||||
sideHd: bitcoin.HDWallet
|
network: network,
|
||||||
.fromSeed(seedBytes, network: networkType)
|
);
|
||||||
.derivePath("m/0'/1"),
|
|
||||||
networkType: networkType,);
|
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LitecoinWallet> create({
|
static Future<LitecoinWallet> create(
|
||||||
required String mnemonic,
|
{required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
String? addressPageType,
|
||||||
ElectrumBalance? initialBalance,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
ElectrumBalance? initialBalance,
|
||||||
int initialChangeAddressIndex = 0
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
}) async {
|
Map<String, int>? initialChangeAddressIndex}) async {
|
||||||
return LitecoinWallet(
|
return LitecoinWallet(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex);
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
|
addressPageType: addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LitecoinWallet> open({
|
static Future<LitecoinWallet> open({
|
||||||
|
@ -84,17 +86,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
final snp = await ElectrumWallletSnapshot.load (name, walletInfo.type, password);
|
final snp =
|
||||||
|
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
|
||||||
return LitecoinWallet(
|
return LitecoinWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: snp.addresses,
|
initialAddresses: snp.addresses,
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex);
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
|
addressPageType: snp.addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,39 +1,27 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
|
||||||
import 'package:cw_bitcoin/utils.dart';
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
part 'litecoin_wallet_addresses.g.dart';
|
part 'litecoin_wallet_addresses.g.dart';
|
||||||
|
|
||||||
class LitecoinWalletAddresses = LitecoinWalletAddressesBase
|
class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses;
|
||||||
with _$LitecoinWalletAddresses;
|
|
||||||
|
|
||||||
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
|
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
||||||
with Store {
|
|
||||||
LitecoinWalletAddressesBase(
|
LitecoinWalletAddressesBase(
|
||||||
WalletInfo walletInfo,
|
WalletInfo walletInfo, {
|
||||||
{required bitcoin.HDWallet mainHd,
|
required super.mainHd,
|
||||||
required bitcoin.HDWallet sideHd,
|
required super.sideHd,
|
||||||
required bitcoin.NetworkType networkType,
|
required super.network,
|
||||||
required ElectrumClient electrumClient,
|
super.initialAddresses,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
super.initialRegularAddressIndex,
|
||||||
int initialRegularAddressIndex = 0,
|
super.initialChangeAddressIndex,
|
||||||
int initialChangeAddressIndex = 0})
|
}) : super(walletInfo);
|
||||||
: super(
|
|
||||||
walletInfo,
|
|
||||||
initialAddresses: initialAddresses,
|
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
|
||||||
mainHd: mainHd,
|
|
||||||
sideHd: sideHd,
|
|
||||||
electrumClient: electrumClient,
|
|
||||||
networkType: networkType);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
String getAddress(
|
||||||
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
{required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
|
||||||
}
|
generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,10 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base.git
|
url: https://github.com/cake-tech/bitcoin_base.git
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
|
blockchain_utils:
|
||||||
|
git:
|
||||||
|
url: https://github.com/rafael-xmr/blockchain_utils
|
||||||
|
ref: cake-update-v1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -28,17 +28,18 @@ part 'bitcoin_cash_wallet.g.dart';
|
||||||
class BitcoinCashWallet = BitcoinCashWalletBase with _$BitcoinCashWallet;
|
class BitcoinCashWallet = BitcoinCashWalletBase with _$BitcoinCashWallet;
|
||||||
|
|
||||||
abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
BitcoinCashWalletBase(
|
BitcoinCashWalletBase({
|
||||||
{required String mnemonic,
|
required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required Uint8List seedBytes,
|
required Uint8List seedBytes,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
String? addressPageType,
|
||||||
ElectrumBalance? initialBalance,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
ElectrumBalance? initialBalance,
|
||||||
int initialChangeAddressIndex = 0})
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
: super(
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
|
}) : super(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -48,40 +49,42 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
currency: CryptoCurrency.bch) {
|
currency: CryptoCurrency.bch) {
|
||||||
walletAddresses = BitcoinCashWalletAddresses(walletInfo,
|
walletAddresses = BitcoinCashWalletAddresses(
|
||||||
electrumClient: electrumClient,
|
walletInfo,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes)
|
sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"),
|
||||||
.derivePath("m/44'/145'/0'/1"),
|
network: network,
|
||||||
networkType: networkType);
|
);
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Future<BitcoinCashWallet> create(
|
static Future<BitcoinCashWallet> create(
|
||||||
{required String mnemonic,
|
{required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
|
String? addressPageType,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
ElectrumBalance? initialBalance,
|
ElectrumBalance? initialBalance,
|
||||||
int initialRegularAddressIndex = 0,
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
int initialChangeAddressIndex = 0}) async {
|
Map<String, int>? initialChangeAddressIndex}) async {
|
||||||
return BitcoinCashWallet(
|
return BitcoinCashWallet(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: await Mnemonic.toSeed(mnemonic),
|
seedBytes: await Mnemonic.toSeed(mnemonic),
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex);
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
|
addressPageType: addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<BitcoinCashWallet> open({
|
static Future<BitcoinCashWallet> open({
|
||||||
|
@ -90,17 +93,20 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password);
|
final snp = await ElectrumWalletSnapshot.load(
|
||||||
|
name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
|
||||||
return BitcoinCashWallet(
|
return BitcoinCashWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: snp.addresses,
|
initialAddresses: snp.addresses,
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await Mnemonic.toSeed(snp.mnemonic),
|
seedBytes: await Mnemonic.toSeed(snp.mnemonic),
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex);
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
|
addressPageType: snp.addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -270,20 +276,18 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
electrumClient: electrumClient, amount: amount, fee: fee);
|
electrumClient: electrumClient, amount: amount, fee: fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
bitbox.ECPair generateKeyPair(
|
bitbox.ECPair generateKeyPair({required bitcoin.HDWallet hd, required int index}) =>
|
||||||
{required bitcoin.HDWallet hd,
|
|
||||||
required int index}) =>
|
|
||||||
bitbox.ECPair.fromWIF(hd.derive(index).wif!);
|
bitbox.ECPair.fromWIF(hd.derive(index).wif!);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int feeAmountForPriority(
|
int feeAmountForPriority(BitcoinTransactionPriority priority, int inputsCount, int outputsCount,
|
||||||
BitcoinTransactionPriority priority, int inputsCount, int outputsCount) =>
|
{int? size}) =>
|
||||||
feeRate(priority) * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
feeRate(priority) * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
||||||
|
|
||||||
int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount) =>
|
int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount, {int? size}) =>
|
||||||
feeRate * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
feeRate * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
||||||
|
|
||||||
int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount}) {
|
int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount, int? size}) {
|
||||||
int inputsCount = 0;
|
int inputsCount = 0;
|
||||||
int totalValue = 0;
|
int totalValue = 0;
|
||||||
|
|
||||||
|
@ -323,9 +327,10 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address = null}) {
|
String signMessage(String message, {String? address = null}) {
|
||||||
final index = address != null
|
final index = address != null
|
||||||
? walletAddresses.addresses
|
? walletAddresses.allAddresses
|
||||||
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
|
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
|
||||||
.index : null;
|
.index
|
||||||
|
: null;
|
||||||
final HD = index == null ? hd : hd.derive(index);
|
final HD = index == null ? hd : hd.derive(index);
|
||||||
return base64Encode(HD.signMessage(message));
|
return base64Encode(HD.signMessage(message));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||||
import 'package:cw_bitcoin/utils.dart';
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
@ -11,24 +10,18 @@ part 'bitcoin_cash_wallet_addresses.g.dart';
|
||||||
class BitcoinCashWalletAddresses = BitcoinCashWalletAddressesBase with _$BitcoinCashWalletAddresses;
|
class BitcoinCashWalletAddresses = BitcoinCashWalletAddressesBase with _$BitcoinCashWalletAddresses;
|
||||||
|
|
||||||
abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
||||||
BitcoinCashWalletAddressesBase(WalletInfo walletInfo,
|
BitcoinCashWalletAddressesBase(
|
||||||
{required bitcoin.HDWallet mainHd,
|
WalletInfo walletInfo, {
|
||||||
required bitcoin.HDWallet sideHd,
|
required super.mainHd,
|
||||||
required bitcoin.NetworkType networkType,
|
required super.sideHd,
|
||||||
required ElectrumClient electrumClient,
|
required super.network,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
super.initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
super.initialRegularAddressIndex,
|
||||||
int initialChangeAddressIndex = 0})
|
super.initialChangeAddressIndex,
|
||||||
: super(walletInfo,
|
}) : super(walletInfo);
|
||||||
initialAddresses: initialAddresses,
|
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
|
||||||
mainHd: mainHd,
|
|
||||||
sideHd: sideHd,
|
|
||||||
electrumClient: electrumClient,
|
|
||||||
networkType: networkType);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
String getAddress(
|
||||||
generateP2PKHAddress(hd: hd, index: index, networkType: networkType);
|
{required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
|
||||||
|
generateP2PKHAddress(hd: hd, index: index, network: network);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,10 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base.git
|
url: https://github.com/cake-tech/bitcoin_base.git
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
|
blockchain_utils:
|
||||||
|
git:
|
||||||
|
url: https://github.com/rafael-xmr/blockchain_utils
|
||||||
|
ref: cake-update-v1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -338,6 +338,12 @@ class CWBitcoin extends Bitcoin {
|
||||||
return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType);
|
return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hasSelectedSilentPayments(Object wallet) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
return bitcoinWallet.walletAddresses.addressPageType == SilentPaymentsAddresType.p2sp;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<BitcoinReceivePageOption> getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all;
|
List<BitcoinReceivePageOption> getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ class AddressValidator extends TextValidator {
|
||||||
return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
|
return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
|
||||||
'|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
|
'|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
|
||||||
case CryptoCurrency.btc:
|
case CryptoCurrency.btc:
|
||||||
return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${bitcoin.SilentPaymentAddress.REGEX.pattern}\$';
|
return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$|^${SilentPaymentAddress.regex.pattern}\$';
|
||||||
case CryptoCurrency.nano:
|
case CryptoCurrency.nano:
|
||||||
return '[0-9a-zA-Z_]';
|
return '[0-9a-zA-Z_]';
|
||||||
case CryptoCurrency.banano:
|
case CryptoCurrency.banano:
|
||||||
|
@ -268,7 +268,7 @@ class AddressValidator extends TextValidator {
|
||||||
'([^0-9a-zA-Z]|^)${P2wpkhAddress.regex.pattern}|\$)'
|
'([^0-9a-zA-Z]|^)${P2wpkhAddress.regex.pattern}|\$)'
|
||||||
'([^0-9a-zA-Z]|^)${P2wshAddress.regex.pattern}|\$)'
|
'([^0-9a-zA-Z]|^)${P2wshAddress.regex.pattern}|\$)'
|
||||||
'([^0-9a-zA-Z]|^)${P2trAddress.regex.pattern}|\$)'
|
'([^0-9a-zA-Z]|^)${P2trAddress.regex.pattern}|\$)'
|
||||||
'|${bitcoin.SilentPaymentAddress.REGEX.pattern}\$';
|
'|${SilentPaymentAddress.regex.pattern}\$';
|
||||||
case CryptoCurrency.ltc:
|
case CryptoCurrency.ltc:
|
||||||
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
|
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||||
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
|
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:cw_core/sync_status.dart';
|
||||||
String syncStatusTitle(SyncStatus syncStatus) {
|
String syncStatusTitle(SyncStatus syncStatus) {
|
||||||
if (syncStatus is SyncingSyncStatus) {
|
if (syncStatus is SyncingSyncStatus) {
|
||||||
return syncStatus.blocksLeft == 1
|
return syncStatus.blocksLeft == 1
|
||||||
? S.current.Block_remaining('${syncStatus.blocksLeft}')
|
? S.current.block_remaining
|
||||||
: S.current.Blocks_remaining('${syncStatus.blocksLeft}');
|
: S.current.Blocks_remaining('${syncStatus.blocksLeft}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,11 @@ class AddressPage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
|
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
|
||||||
|
if (option is BitcoinReceivePageOption) {
|
||||||
|
addressListViewModel.setAddressType(option.toType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case ReceivePageOption.anonPayInvoice:
|
case ReceivePageOption.anonPayInvoice:
|
||||||
Navigator.pushNamed(
|
Navigator.pushNamed(
|
||||||
|
|
|
@ -45,7 +45,7 @@ class PresentReceiveOptionPicker extends StatelessWidget {
|
||||||
fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', color: color),
|
fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', color: color),
|
||||||
),
|
),
|
||||||
Observer(
|
Observer(
|
||||||
builder: (_) => Text(describeOption(receiveOptionViewModel.selectedReceiveOption),
|
builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(),
|
||||||
style: TextStyle(fontSize: 10.0, fontWeight: FontWeight.w500, color: color)))
|
style: TextStyle(fontSize: 10.0, fontWeight: FontWeight.w500, color: color)))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -101,7 +101,7 @@ class PresentReceiveOptionPicker extends StatelessWidget {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(describeOption(option),
|
Text(option.toString(),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style: textSmall(
|
style: textSmall(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
|
|
|
@ -13,8 +13,7 @@ class AddressEditOrCreatePage extends BasePage {
|
||||||
: _formKey = GlobalKey<FormState>(),
|
: _formKey = GlobalKey<FormState>(),
|
||||||
_labelController = TextEditingController(),
|
_labelController = TextEditingController(),
|
||||||
super() {
|
super() {
|
||||||
_labelController.addListener(
|
_labelController.addListener(() => addressEditOrCreateViewModel.label = _labelController.text);
|
||||||
() => addressEditOrCreateViewModel.label = _labelController.text);
|
|
||||||
_labelController.text = addressEditOrCreateViewModel.label;
|
_labelController.text = addressEditOrCreateViewModel.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,10 +54,8 @@ class AddressEditOrCreatePage extends BasePage {
|
||||||
: S.of(context).new_subaddress_create,
|
: S.of(context).new_subaddress_create,
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
isLoading:
|
isLoading: addressEditOrCreateViewModel.state is AddressIsSaving,
|
||||||
addressEditOrCreateViewModel.state is AddressIsSaving,
|
isDisabled: addressEditOrCreateViewModel.label?.isEmpty ?? true,
|
||||||
isDisabled:
|
|
||||||
addressEditOrCreateViewModel.label?.isEmpty ?? true,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -70,14 +67,13 @@ class AddressEditOrCreatePage extends BasePage {
|
||||||
if (_isEffectsInstalled) {
|
if (_isEffectsInstalled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
reaction((_) => addressEditOrCreateViewModel.state,
|
reaction((_) => addressEditOrCreateViewModel.state, (AddressEditOrCreateState state) {
|
||||||
(AddressEditOrCreateState state) {
|
if (state is AddressSavedSuccessfully) {
|
||||||
if (state is AddressSavedSuccessfully) {
|
WidgetsBinding.instance.addPostFrameCallback((_) => Navigator.of(context).pop());
|
||||||
WidgetsBinding.instance
|
}
|
||||||
.addPostFrameCallback((_) => Navigator.of(context).pop());
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_isEffectsInstalled = true;
|
_isEffectsInstalled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:eth_sig_util/util/utils.dart';
|
import 'package:eth_sig_util/util/utils.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/entities/provider_types.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
|
|
||||||
part 'dashboard_view_model.g.dart';
|
part 'dashboard_view_model.g.dart';
|
||||||
|
|
||||||
|
@ -273,12 +273,10 @@ abstract class DashboardViewModelBase with Store {
|
||||||
@observable
|
@observable
|
||||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
|
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
|
||||||
|
|
||||||
bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
|
bool get hasRescan =>
|
||||||
// bool get hasRescan =>
|
(wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet)) ||
|
||||||
// (wallet.type == WalletType.bitcoin &&
|
wallet.type == WalletType.monero ||
|
||||||
// wallet.walletAddresses.addressPageType == bitcoin.AddressType.p2sp) ||
|
wallet.type == WalletType.haven;
|
||||||
// wallet.type == WalletType.monero ||
|
|
||||||
// wallet.type == WalletType.haven;
|
|
||||||
|
|
||||||
final KeyService keyService;
|
final KeyService keyService;
|
||||||
|
|
||||||
|
@ -340,15 +338,13 @@ abstract class DashboardViewModelBase with Store {
|
||||||
bool hasExchangeAction;
|
bool hasExchangeAction;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get isEnabledBuyAction =>
|
bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
|
||||||
!settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
|
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
bool hasBuyAction;
|
bool hasBuyAction;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get isEnabledSellAction =>
|
bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty;
|
||||||
!settingsStore.disableSell && availableSellProviders.isNotEmpty;
|
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
bool hasSellAction;
|
bool hasSellAction;
|
||||||
|
@ -473,7 +469,8 @@ abstract class DashboardViewModelBase with Store {
|
||||||
|
|
||||||
Future<List<String>> checkAffectedWallets() async {
|
Future<List<String>> checkAffectedWallets() async {
|
||||||
// await load file
|
// await load file
|
||||||
final vulnerableSeedsString = await rootBundle.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
|
final vulnerableSeedsString = await rootBundle
|
||||||
|
.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
|
||||||
final vulnerableSeeds = vulnerableSeedsString.split("\n");
|
final vulnerableSeeds = vulnerableSeedsString.split("\n");
|
||||||
|
|
||||||
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
|
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
|
||||||
|
|
|
@ -387,8 +387,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
wallet.type == WalletType.bitcoinCash;
|
wallet.type == WalletType.bitcoinCash;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get isAutoGenerateSubaddressEnabled =>
|
bool get isAutoGenerateSubaddressEnabled => wallet.type == WalletType.bitcoin
|
||||||
_settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
|
? !bitcoin!.hasSelectedSilentPayments(wallet)
|
||||||
|
: _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
|
||||||
|
|
||||||
List<ListItem> _baseItems;
|
List<ListItem> _baseItems;
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "موضوع البيتكوين الظلام",
|
"bitcoin_dark_theme": "موضوع البيتكوين الظلام",
|
||||||
"bitcoin_light_theme": "موضوع البيتكوين الخفيفة",
|
"bitcoin_light_theme": "موضوع البيتكوين الخفيفة",
|
||||||
"bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.",
|
"bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.",
|
||||||
|
"block_remaining": "1 كتلة متبقية",
|
||||||
"Blocks_remaining": "بلوك متبقي ${status}",
|
"Blocks_remaining": "بلوك متبقي ${status}",
|
||||||
"bright_theme": "مشرق",
|
"bright_theme": "مشرق",
|
||||||
"buy": "اشتري",
|
"buy": "اشتري",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Тъмна тема за биткойн",
|
"bitcoin_dark_theme": "Тъмна тема за биткойн",
|
||||||
"bitcoin_light_theme": "Лека биткойн тема",
|
"bitcoin_light_theme": "Лека биткойн тема",
|
||||||
"bitcoin_payments_require_1_confirmation": "Плащанията с Bitcoin изискват потвърждение, което може да отнеме 20 минути или повече. Благодарим за търпението! Ще получите имейл, когато плащането е потвърдено.",
|
"bitcoin_payments_require_1_confirmation": "Плащанията с Bitcoin изискват потвърждение, което може да отнеме 20 минути или повече. Благодарим за търпението! Ще получите имейл, когато плащането е потвърдено.",
|
||||||
|
"block_remaining": "1 блок останал",
|
||||||
"Blocks_remaining": "${status} оставащи блока",
|
"Blocks_remaining": "${status} оставащи блока",
|
||||||
"bright_theme": "Ярко",
|
"bright_theme": "Ярко",
|
||||||
"buy": "Купуване",
|
"buy": "Купуване",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Tmavé téma bitcoinů",
|
"bitcoin_dark_theme": "Tmavé téma bitcoinů",
|
||||||
"bitcoin_light_theme": "Světlé téma bitcoinů",
|
"bitcoin_light_theme": "Světlé téma bitcoinů",
|
||||||
"bitcoin_payments_require_1_confirmation": "U plateb Bitcoinem je vyžadováno alespoň 1 potvrzení, což může trvat 20 minut i déle. Děkujeme za vaši trpělivost! Až bude platba potvrzena, budete informováni e-mailem.",
|
"bitcoin_payments_require_1_confirmation": "U plateb Bitcoinem je vyžadováno alespoň 1 potvrzení, což může trvat 20 minut i déle. Děkujeme za vaši trpělivost! Až bude platba potvrzena, budete informováni e-mailem.",
|
||||||
|
"block_remaining": "1 blok zbývající",
|
||||||
"Blocks_remaining": "Zbývá ${status} bloků",
|
"Blocks_remaining": "Zbývá ${status} bloků",
|
||||||
"bright_theme": "Jasný",
|
"bright_theme": "Jasný",
|
||||||
"buy": "Koupit",
|
"buy": "Koupit",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Dunkles Bitcoin-Thema",
|
"bitcoin_dark_theme": "Dunkles Bitcoin-Thema",
|
||||||
"bitcoin_light_theme": "Bitcoin Light-Thema",
|
"bitcoin_light_theme": "Bitcoin Light-Thema",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.",
|
||||||
|
"block_remaining": "1 Block verbleibend",
|
||||||
"Blocks_remaining": "${status} verbleibende Blöcke",
|
"Blocks_remaining": "${status} verbleibende Blöcke",
|
||||||
"bright_theme": "Strahlend hell",
|
"bright_theme": "Strahlend hell",
|
||||||
"buy": "Kaufen",
|
"buy": "Kaufen",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin Dark Theme",
|
"bitcoin_dark_theme": "Bitcoin Dark Theme",
|
||||||
"bitcoin_light_theme": "Bitcoin Light Theme",
|
"bitcoin_light_theme": "Bitcoin Light Theme",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin payments require 1 confirmation, which can take 20 minutes or longer. Thanks for your patience! You will be emailed when the payment is confirmed.",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin payments require 1 confirmation, which can take 20 minutes or longer. Thanks for your patience! You will be emailed when the payment is confirmed.",
|
||||||
|
"block_remaining": "1 Block Remaining",
|
||||||
"Blocks_remaining": "${status} Blocks Remaining",
|
"Blocks_remaining": "${status} Blocks Remaining",
|
||||||
"bright_theme": "Bright",
|
"bright_theme": "Bright",
|
||||||
"buy": "Buy",
|
"buy": "Buy",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Tema oscuro de Bitcoin",
|
"bitcoin_dark_theme": "Tema oscuro de Bitcoin",
|
||||||
"bitcoin_light_theme": "Tema de la luz de Bitcoin",
|
"bitcoin_light_theme": "Tema de la luz 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.",
|
"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",
|
"Blocks_remaining": "${status} Bloques restantes",
|
||||||
"bright_theme": "Brillante",
|
"bright_theme": "Brillante",
|
||||||
"buy": "Comprar",
|
"buy": "Comprar",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Thème sombre Bitcoin",
|
"bitcoin_dark_theme": "Thème sombre Bitcoin",
|
||||||
"bitcoin_light_theme": "Thème léger Bitcoin",
|
"bitcoin_light_theme": "Thème léger Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Les paiements Bitcoin nécessitent 1 confirmation, ce qui peut prendre 20 minutes ou plus. Merci pour votre patience ! Vous serez averti par e-mail lorsque le paiement sera confirmé.",
|
"bitcoin_payments_require_1_confirmation": "Les paiements Bitcoin nécessitent 1 confirmation, ce qui peut prendre 20 minutes ou plus. Merci pour votre patience ! Vous serez averti par e-mail lorsque le paiement sera confirmé.",
|
||||||
|
"block_remaining": "1 bloc restant",
|
||||||
"Blocks_remaining": "Blocs Restants : ${status}",
|
"Blocks_remaining": "Blocs Restants : ${status}",
|
||||||
"bright_theme": "Vif",
|
"bright_theme": "Vif",
|
||||||
"buy": "Acheter",
|
"buy": "Acheter",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin Dark Jigo",
|
"bitcoin_dark_theme": "Bitcoin Dark Jigo",
|
||||||
"bitcoin_light_theme": "Jigon Hasken Bitcoin",
|
"bitcoin_light_theme": "Jigon Hasken Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.",
|
"bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.",
|
||||||
|
"block_remaining": "1 toshe ragowar",
|
||||||
"Blocks_remaining": "${status} Katanga ya rage",
|
"Blocks_remaining": "${status} Katanga ya rage",
|
||||||
"bright_theme": "Mai haske",
|
"bright_theme": "Mai haske",
|
||||||
"buy": "Sayi",
|
"buy": "Sayi",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "बिटकॉइन डार्क थीम",
|
"bitcoin_dark_theme": "बिटकॉइन डार्क थीम",
|
||||||
"bitcoin_light_theme": "बिटकॉइन लाइट थीम",
|
"bitcoin_light_theme": "बिटकॉइन लाइट थीम",
|
||||||
"bitcoin_payments_require_1_confirmation": "बिटकॉइन भुगतान के लिए 1 पुष्टिकरण की आवश्यकता होती है, जिसमें 20 मिनट या अधिक समय लग सकता है। आपके धैर्य के लिए धन्यवाद! भुगतान की पुष्टि होने पर आपको ईमेल किया जाएगा।",
|
"bitcoin_payments_require_1_confirmation": "बिटकॉइन भुगतान के लिए 1 पुष्टिकरण की आवश्यकता होती है, जिसमें 20 मिनट या अधिक समय लग सकता है। आपके धैर्य के लिए धन्यवाद! भुगतान की पुष्टि होने पर आपको ईमेल किया जाएगा।",
|
||||||
|
"block_remaining": "1 ब्लॉक शेष",
|
||||||
"Blocks_remaining": "${status} शेष रहते हैं",
|
"Blocks_remaining": "${status} शेष रहते हैं",
|
||||||
"bright_theme": "उज्ज्वल",
|
"bright_theme": "उज्ज्वल",
|
||||||
"buy": "खरीदें",
|
"buy": "खरीदें",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin Tamna tema",
|
"bitcoin_dark_theme": "Bitcoin Tamna tema",
|
||||||
"bitcoin_light_theme": "Bitcoin Light Theme",
|
"bitcoin_light_theme": "Bitcoin Light Theme",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin plaćanja zahtijevaju 1 potvrdu, što može potrajati 20 minuta ili dulje. Hvala na Vašem strpljenju! Dobit ćete e-poruku kada plaćanje bude potvrđeno.",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin plaćanja zahtijevaju 1 potvrdu, što može potrajati 20 minuta ili dulje. Hvala na Vašem strpljenju! Dobit ćete e-poruku kada plaćanje bude potvrđeno.",
|
||||||
|
"block_remaining": "Preostalo 1 blok",
|
||||||
"Blocks_remaining": "${status} preostalih blokova",
|
"Blocks_remaining": "${status} preostalih blokova",
|
||||||
"bright_theme": "Jarka",
|
"bright_theme": "Jarka",
|
||||||
"buy": "Kupi",
|
"buy": "Kupi",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Tema Gelap Bitcoin",
|
"bitcoin_dark_theme": "Tema Gelap Bitcoin",
|
||||||
"bitcoin_light_theme": "Tema Cahaya Bitcoin",
|
"bitcoin_light_theme": "Tema Cahaya Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Pembayaran Bitcoin memerlukan 1 konfirmasi, yang bisa memakan waktu 20 menit atau lebih. Terima kasih atas kesabaran Anda! Anda akan diemail saat pembayaran dikonfirmasi.",
|
"bitcoin_payments_require_1_confirmation": "Pembayaran Bitcoin memerlukan 1 konfirmasi, yang bisa memakan waktu 20 menit atau lebih. Terima kasih atas kesabaran Anda! Anda akan diemail saat pembayaran dikonfirmasi.",
|
||||||
|
"block_remaining": "1 blok tersisa",
|
||||||
"Blocks_remaining": "${status} Blok Tersisa",
|
"Blocks_remaining": "${status} Blok Tersisa",
|
||||||
"bright_theme": "Cerah",
|
"bright_theme": "Cerah",
|
||||||
"buy": "Beli",
|
"buy": "Beli",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Tema oscuro di Bitcoin",
|
"bitcoin_dark_theme": "Tema oscuro di Bitcoin",
|
||||||
"bitcoin_light_theme": "Tema luce Bitcoin",
|
"bitcoin_light_theme": "Tema luce Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "I pagamenti in bitcoin richiedono 1 conferma, che può richiedere 20 minuti o più. Grazie per la vostra pazienza! Riceverai un'e-mail quando il pagamento sarà confermato.",
|
"bitcoin_payments_require_1_confirmation": "I pagamenti in bitcoin richiedono 1 conferma, che può richiedere 20 minuti o più. Grazie per la vostra pazienza! Riceverai un'e-mail quando il pagamento sarà confermato.",
|
||||||
|
"block_remaining": "1 blocco rimanente",
|
||||||
"Blocks_remaining": "${status} Blocchi Rimanenti",
|
"Blocks_remaining": "${status} Blocchi Rimanenti",
|
||||||
"bright_theme": "Colorato",
|
"bright_theme": "Colorato",
|
||||||
"buy": "Comprare",
|
"buy": "Comprare",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "ビットコインダークテーマ",
|
"bitcoin_dark_theme": "ビットコインダークテーマ",
|
||||||
"bitcoin_light_theme": "ビットコインライトテーマ",
|
"bitcoin_light_theme": "ビットコインライトテーマ",
|
||||||
"bitcoin_payments_require_1_confirmation": "ビットコインの支払いには 1 回の確認が必要で、これには 20 分以上かかる場合があります。お待ち頂きまして、ありがとうございます!支払いが確認されると、メールが送信されます。",
|
"bitcoin_payments_require_1_confirmation": "ビットコインの支払いには 1 回の確認が必要で、これには 20 分以上かかる場合があります。お待ち頂きまして、ありがとうございます!支払いが確認されると、メールが送信されます。",
|
||||||
|
"block_remaining": "残り1ブロック",
|
||||||
"Blocks_remaining": "${status} 残りのブロック",
|
"Blocks_remaining": "${status} 残りのブロック",
|
||||||
"bright_theme": "明るい",
|
"bright_theme": "明るい",
|
||||||
"buy": "購入",
|
"buy": "購入",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "비트코인 다크 테마",
|
"bitcoin_dark_theme": "비트코인 다크 테마",
|
||||||
"bitcoin_light_theme": "비트코인 라이트 테마",
|
"bitcoin_light_theme": "비트코인 라이트 테마",
|
||||||
"bitcoin_payments_require_1_confirmation": "비트코인 결제는 1번의 확인이 필요하며 20분 이상이 소요될 수 있습니다. 기다려 주셔서 감사합니다! 결제가 확인되면 이메일이 전송됩니다.",
|
"bitcoin_payments_require_1_confirmation": "비트코인 결제는 1번의 확인이 필요하며 20분 이상이 소요될 수 있습니다. 기다려 주셔서 감사합니다! 결제가 확인되면 이메일이 전송됩니다.",
|
||||||
|
"block_remaining": "남은 블록 1 개",
|
||||||
"Blocks_remaining": "${status} 남은 블록",
|
"Blocks_remaining": "${status} 남은 블록",
|
||||||
"bright_theme": "선명한",
|
"bright_theme": "선명한",
|
||||||
"buy": "구입",
|
"buy": "구입",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin Dark Theme",
|
"bitcoin_dark_theme": "Bitcoin Dark Theme",
|
||||||
"bitcoin_light_theme": "Bitcoin Light အပြင်အဆင်",
|
"bitcoin_light_theme": "Bitcoin Light အပြင်အဆင်",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin ငွေပေးချေမှုများသည် မိနစ် 20 သို့မဟုတ် ထို့ထက်ပိုကြာနိုင်သည် 1 အတည်ပြုချက် လိုအပ်သည်။ မင်းရဲ့စိတ်ရှည်မှုအတွက် ကျေးဇူးတင်ပါတယ်။ ငွေပေးချေမှုကို အတည်ပြုပြီးသောအခါ သင့်ထံ အီးမေးလ်ပို့ပါမည်။",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin ငွေပေးချေမှုများသည် မိနစ် 20 သို့မဟုတ် ထို့ထက်ပိုကြာနိုင်သည် 1 အတည်ပြုချက် လိုအပ်သည်။ မင်းရဲ့စိတ်ရှည်မှုအတွက် ကျေးဇူးတင်ပါတယ်။ ငွေပေးချေမှုကို အတည်ပြုပြီးသောအခါ သင့်ထံ အီးမေးလ်ပို့ပါမည်။",
|
||||||
|
"block_remaining": "ကျန်ရှိနေသေးသော block",
|
||||||
"Blocks_remaining": "${status} ဘလောက်များ ကျန်နေပါသည်။",
|
"Blocks_remaining": "${status} ဘလောက်များ ကျန်နေပါသည်။",
|
||||||
"bright_theme": "တောက်ပ",
|
"bright_theme": "တောက်ပ",
|
||||||
"buy": "ဝယ်ပါ။",
|
"buy": "ဝယ်ပါ။",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin donker thema",
|
"bitcoin_dark_theme": "Bitcoin donker thema",
|
||||||
"bitcoin_light_theme": "Bitcoin Light-thema",
|
"bitcoin_light_theme": "Bitcoin Light-thema",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin-betalingen vereisen 1 bevestiging, wat 20 minuten of langer kan duren. Dank voor uw geduld! U ontvangt een e-mail wanneer de betaling is bevestigd.",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin-betalingen vereisen 1 bevestiging, wat 20 minuten of langer kan duren. Dank voor uw geduld! U ontvangt een e-mail wanneer de betaling is bevestigd.",
|
||||||
|
"block_remaining": "1 blok resterend",
|
||||||
"Blocks_remaining": "${status} Resterende blokken",
|
"Blocks_remaining": "${status} Resterende blokken",
|
||||||
"bright_theme": "Helder",
|
"bright_theme": "Helder",
|
||||||
"buy": "Kopen",
|
"buy": "Kopen",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Ciemny motyw Bitcoina",
|
"bitcoin_dark_theme": "Ciemny motyw Bitcoina",
|
||||||
"bitcoin_light_theme": "Lekki motyw Bitcoin",
|
"bitcoin_light_theme": "Lekki motyw Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Płatności Bitcoin wymagają 1 potwierdzenia, co może zająć 20 minut lub dłużej. Dziękuję za cierpliwość! Otrzymasz wiadomość e-mail, gdy płatność zostanie potwierdzona.",
|
"bitcoin_payments_require_1_confirmation": "Płatności Bitcoin wymagają 1 potwierdzenia, co może zająć 20 minut lub dłużej. Dziękuję za cierpliwość! Otrzymasz wiadomość e-mail, gdy płatność zostanie potwierdzona.",
|
||||||
|
"block_remaining": "1 blok pozostałym",
|
||||||
"Blocks_remaining": "Pozostało ${status} bloków",
|
"Blocks_remaining": "Pozostało ${status} bloków",
|
||||||
"bright_theme": "Biały",
|
"bright_theme": "Biały",
|
||||||
"buy": "Kup",
|
"buy": "Kup",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Tema escuro Bitcoin",
|
"bitcoin_dark_theme": "Tema escuro Bitcoin",
|
||||||
"bitcoin_light_theme": "Tema claro de bitcoin",
|
"bitcoin_light_theme": "Tema claro de bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Os pagamentos em Bitcoin exigem 1 confirmação, o que pode levar 20 minutos ou mais. Obrigado pela sua paciência! Você receberá um e-mail quando o pagamento for confirmado.",
|
"bitcoin_payments_require_1_confirmation": "Os pagamentos em Bitcoin exigem 1 confirmação, o que pode levar 20 minutos ou mais. Obrigado pela sua paciência! Você receberá um e-mail quando o pagamento for confirmado.",
|
||||||
|
"block_remaining": "1 bloco restante",
|
||||||
"Blocks_remaining": "${status} blocos restantes",
|
"Blocks_remaining": "${status} blocos restantes",
|
||||||
"bright_theme": "Brilhante",
|
"bright_theme": "Brilhante",
|
||||||
"buy": "Comprar",
|
"buy": "Comprar",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Биткойн Темная тема",
|
"bitcoin_dark_theme": "Биткойн Темная тема",
|
||||||
"bitcoin_light_theme": "Легкая биткойн-тема",
|
"bitcoin_light_theme": "Легкая биткойн-тема",
|
||||||
"bitcoin_payments_require_1_confirmation": "Биткойн-платежи требуют 1 подтверждения, что может занять 20 минут или дольше. Спасибо тебе за твое терпение! Вы получите электронное письмо, когда платеж будет подтвержден.",
|
"bitcoin_payments_require_1_confirmation": "Биткойн-платежи требуют 1 подтверждения, что может занять 20 минут или дольше. Спасибо тебе за твое терпение! Вы получите электронное письмо, когда платеж будет подтвержден.",
|
||||||
|
"block_remaining": "1 Блок остался",
|
||||||
"Blocks_remaining": "${status} Осталось блоков",
|
"Blocks_remaining": "${status} Осталось блоков",
|
||||||
"bright_theme": "Яркая",
|
"bright_theme": "Яркая",
|
||||||
"buy": "Купить",
|
"buy": "Купить",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "ธีมมืด Bitcoin",
|
"bitcoin_dark_theme": "ธีมมืด Bitcoin",
|
||||||
"bitcoin_light_theme": "ธีมแสง Bitcoin",
|
"bitcoin_light_theme": "ธีมแสง Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "การชำระเงินด้วย Bitcoin ต้องการการยืนยัน 1 ครั้ง ซึ่งอาจใช้เวลา 20 นาทีหรือนานกว่านั้น ขอบคุณสำหรับความอดทนของคุณ! คุณจะได้รับอีเมลเมื่อการชำระเงินได้รับการยืนยัน",
|
"bitcoin_payments_require_1_confirmation": "การชำระเงินด้วย Bitcoin ต้องการการยืนยัน 1 ครั้ง ซึ่งอาจใช้เวลา 20 นาทีหรือนานกว่านั้น ขอบคุณสำหรับความอดทนของคุณ! คุณจะได้รับอีเมลเมื่อการชำระเงินได้รับการยืนยัน",
|
||||||
|
"block_remaining": "เหลือ 1 บล็อก",
|
||||||
"Blocks_remaining": "${status} บล็อกที่เหลืออยู่",
|
"Blocks_remaining": "${status} บล็อกที่เหลืออยู่",
|
||||||
"bright_theme": "สดใส",
|
"bright_theme": "สดใส",
|
||||||
"buy": "ซื้อ",
|
"buy": "ซื้อ",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin Madilim na Tema",
|
"bitcoin_dark_theme": "Bitcoin Madilim na Tema",
|
||||||
"bitcoin_light_theme": "Tema ng ilaw ng bitcoin",
|
"bitcoin_light_theme": "Tema ng ilaw ng bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Ang mga pagbabayad sa Bitcoin ay nangangailangan ng 1 kumpirmasyon, na maaaring tumagal ng 20 minuto o mas mahaba. Salamat sa iyong pasensya! Mag -email ka kapag nakumpirma ang pagbabayad.",
|
"bitcoin_payments_require_1_confirmation": "Ang mga pagbabayad sa Bitcoin ay nangangailangan ng 1 kumpirmasyon, na maaaring tumagal ng 20 minuto o mas mahaba. Salamat sa iyong pasensya! Mag -email ka kapag nakumpirma ang pagbabayad.",
|
||||||
|
"block_remaining": "1 bloke ang natitira",
|
||||||
"Blocks_remaining": "Ang natitirang ${status} ay natitira",
|
"Blocks_remaining": "Ang natitirang ${status} ay natitira",
|
||||||
"bright_theme": "Maliwanag",
|
"bright_theme": "Maliwanag",
|
||||||
"buy": "Bilhin",
|
"buy": "Bilhin",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin Karanlık Teması",
|
"bitcoin_dark_theme": "Bitcoin Karanlık Teması",
|
||||||
"bitcoin_light_theme": "Bitcoin Hafif Tema",
|
"bitcoin_light_theme": "Bitcoin Hafif Tema",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin ödemeleri, 20 dakika veya daha uzun sürebilen 1 onay gerektirir. Sabrınız için teşekkürler! Ödeme onaylandığında e-posta ile bilgilendirileceksiniz.",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin ödemeleri, 20 dakika veya daha uzun sürebilen 1 onay gerektirir. Sabrınız için teşekkürler! Ödeme onaylandığında e-posta ile bilgilendirileceksiniz.",
|
||||||
|
"block_remaining": "Kalan 1 blok",
|
||||||
"Blocks_remaining": "${status} Blok Kaldı",
|
"Blocks_remaining": "${status} Blok Kaldı",
|
||||||
"bright_theme": "Parlak",
|
"bright_theme": "Parlak",
|
||||||
"buy": "Alış",
|
"buy": "Alış",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Темна тема Bitcoin",
|
"bitcoin_dark_theme": "Темна тема Bitcoin",
|
||||||
"bitcoin_light_theme": "Світла тема Bitcoin",
|
"bitcoin_light_theme": "Світла тема Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Платежі Bitcoin потребують 1 підтвердження, яке може зайняти 20 хвилин або більше. Дякую за Ваше терпіння! Ви отримаєте електронний лист, коли платіж буде підтверджено.",
|
"bitcoin_payments_require_1_confirmation": "Платежі Bitcoin потребують 1 підтвердження, яке може зайняти 20 хвилин або більше. Дякую за Ваше терпіння! Ви отримаєте електронний лист, коли платіж буде підтверджено.",
|
||||||
|
"block_remaining": "1 блок, що залишився",
|
||||||
"Blocks_remaining": "${status} Залишилось блоків",
|
"Blocks_remaining": "${status} Залишилось блоків",
|
||||||
"bright_theme": "Яскрава",
|
"bright_theme": "Яскрава",
|
||||||
"buy": "Купити",
|
"buy": "Купити",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "بٹ کوائن ڈارک تھیم",
|
"bitcoin_dark_theme": "بٹ کوائن ڈارک تھیم",
|
||||||
"bitcoin_light_theme": "بٹ کوائن لائٹ تھیم",
|
"bitcoin_light_theme": "بٹ کوائن لائٹ تھیم",
|
||||||
"bitcoin_payments_require_1_confirmation": "بٹ کوائن کی ادائیگی میں 1 تصدیق کی ضرورت ہوتی ہے ، جس میں 20 منٹ یا اس سے زیادہ وقت لگ سکتا ہے۔ آپ کے صبر کا شکریہ! ادائیگی کی تصدیق ہونے پر آپ کو ای میل کیا جائے گا۔",
|
"bitcoin_payments_require_1_confirmation": "بٹ کوائن کی ادائیگی میں 1 تصدیق کی ضرورت ہوتی ہے ، جس میں 20 منٹ یا اس سے زیادہ وقت لگ سکتا ہے۔ آپ کے صبر کا شکریہ! ادائیگی کی تصدیق ہونے پر آپ کو ای میل کیا جائے گا۔",
|
||||||
|
"block_remaining": "1 بلاک باقی",
|
||||||
"Blocks_remaining": "${status} بلاکس باقی ہیں۔",
|
"Blocks_remaining": "${status} بلاکس باقی ہیں۔",
|
||||||
"bright_theme": "روشن",
|
"bright_theme": "روشن",
|
||||||
"buy": "خریدنے",
|
"buy": "خریدنے",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "Bitcoin Dark Akori",
|
"bitcoin_dark_theme": "Bitcoin Dark Akori",
|
||||||
"bitcoin_light_theme": "Bitcoin Light Akori",
|
"bitcoin_light_theme": "Bitcoin Light Akori",
|
||||||
"bitcoin_payments_require_1_confirmation": "Àwọn àránṣẹ́ Bitcoin nílò ìjẹ́rìísí kan. Ó lè lo ìṣéjú ogun tàbí ìṣéjú jù. A dúpẹ́ fún sùúrù yín! Ẹ máa gba ímeèlì t'ó bá jẹ́rìísí àránṣẹ́ náà.",
|
"bitcoin_payments_require_1_confirmation": "Àwọn àránṣẹ́ Bitcoin nílò ìjẹ́rìísí kan. Ó lè lo ìṣéjú ogun tàbí ìṣéjú jù. A dúpẹ́ fún sùúrù yín! Ẹ máa gba ímeèlì t'ó bá jẹ́rìísí àránṣẹ́ náà.",
|
||||||
|
"block_remaining": "1 bulọọki to ku",
|
||||||
"Blocks_remaining": "Àkójọpọ̀ ${status} kikù",
|
"Blocks_remaining": "Àkójọpọ̀ ${status} kikù",
|
||||||
"bright_theme": "Funfun",
|
"bright_theme": "Funfun",
|
||||||
"buy": "Rà",
|
"buy": "Rà",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"bitcoin_dark_theme": "比特币黑暗主题",
|
"bitcoin_dark_theme": "比特币黑暗主题",
|
||||||
"bitcoin_light_theme": "比特币浅色主题",
|
"bitcoin_light_theme": "比特币浅色主题",
|
||||||
"bitcoin_payments_require_1_confirmation": "比特币支付需要 1 次确认,这可能需要 20 分钟或更长时间。谢谢你的耐心!确认付款后,您将收到电子邮件。",
|
"bitcoin_payments_require_1_confirmation": "比特币支付需要 1 次确认,这可能需要 20 分钟或更长时间。谢谢你的耐心!确认付款后,您将收到电子邮件。",
|
||||||
|
"block_remaining": "剩下1个块",
|
||||||
"Blocks_remaining": "${status} 剩余的块",
|
"Blocks_remaining": "${status} 剩余的块",
|
||||||
"bright_theme": "明亮",
|
"bright_theme": "明亮",
|
||||||
"buy": "购买",
|
"buy": "购买",
|
||||||
|
|
|
@ -67,6 +67,7 @@ import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:hive/hive.dart';""";
|
import 'package:hive/hive.dart';""";
|
||||||
const bitcoinCWHeaders = """
|
const bitcoinCWHeaders = """
|
||||||
|
import 'package:cw_bitcoin/bitcoin_receive_page_option.dart';
|
||||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
|
@ -77,6 +78,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||||
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
""";
|
""";
|
||||||
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
||||||
|
@ -135,6 +137,11 @@ abstract class Bitcoin {
|
||||||
TransactionPriority getLitecoinTransactionPriorityMedium();
|
TransactionPriority getLitecoinTransactionPriorityMedium();
|
||||||
TransactionPriority getBitcoinTransactionPrioritySlow();
|
TransactionPriority getBitcoinTransactionPrioritySlow();
|
||||||
TransactionPriority getLitecoinTransactionPrioritySlow();
|
TransactionPriority getLitecoinTransactionPrioritySlow();
|
||||||
|
|
||||||
|
Future<void> setAddressType(Object wallet, dynamic option);
|
||||||
|
BitcoinReceivePageOption getSelectedAddressType(Object wallet);
|
||||||
|
bool hasSelectedSilentPayments(Object wallet);
|
||||||
|
List<BitcoinReceivePageOption> getBitcoinReceivePageOptions();
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue