mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
feat: pull changes from silent payments
This commit is contained in:
parent
e092509264
commit
5c205e4cc8
87 changed files with 1750 additions and 847 deletions
|
@ -4,20 +4,16 @@ import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
|||
|
||||
String addressFromOutput(Uint8List script, bitcoin.NetworkType networkType) {
|
||||
try {
|
||||
return bitcoin.P2PKH(
|
||||
data: PaymentData(output: script),
|
||||
network: networkType)
|
||||
.data
|
||||
.address!;
|
||||
return bitcoin.P2PKH(data: PaymentData(output: script), network: networkType).data.address!;
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
return bitcoin.P2WPKH(
|
||||
data: PaymentData(output: script),
|
||||
network: networkType)
|
||||
.data
|
||||
.address!;
|
||||
} catch(_) {}
|
||||
return bitcoin.P2WPKH(data: PaymentData(output: script), network: networkType).data.address!;
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
return bitcoin.P2TR(data: PaymentData(output: script), network: networkType).data.address!;
|
||||
} catch (_) {}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
import 'dart:convert';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
|
||||
class BitcoinAddressRecord {
|
||||
BitcoinAddressRecord(this.address,
|
||||
{required this.index, this.isHidden = false, bool isUsed = false})
|
||||
: _isUsed = isUsed;
|
||||
{required this.index,
|
||||
this.isHidden = false,
|
||||
bool isUsed = false,
|
||||
this.silentAddressLabel,
|
||||
this.silentPaymentTweak,
|
||||
this.type})
|
||||
: _isUsed = isUsed;
|
||||
|
||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
return BitcoinAddressRecord(
|
||||
decoded['address'] as String,
|
||||
index: decoded['index'] as int,
|
||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false);
|
||||
return BitcoinAddressRecord(decoded['address'] as String,
|
||||
index: decoded['index'] as int,
|
||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||
silentAddressLabel: decoded['silentAddressLabel'] as String?,
|
||||
silentPaymentTweak: decoded['silentPaymentTweak'] as String?,
|
||||
type: decoded['type'] != null && decoded['type'] != ''
|
||||
? AddressType.values.firstWhere((type) => type.toString() == decoded['type'] as String)
|
||||
: null);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) =>
|
||||
o is BitcoinAddressRecord && address == o.address;
|
||||
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
|
||||
|
||||
final String address;
|
||||
final bool isHidden;
|
||||
final String? silentAddressLabel;
|
||||
final String? silentPaymentTweak;
|
||||
final int index;
|
||||
bool get isUsed => _isUsed;
|
||||
|
||||
|
@ -31,10 +42,15 @@ class BitcoinAddressRecord {
|
|||
|
||||
void setAsUsed() => _isUsed = true;
|
||||
|
||||
String toJSON() =>
|
||||
json.encode({
|
||||
AddressType? type;
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'address': address,
|
||||
'index': index,
|
||||
'isHidden': isHidden,
|
||||
'isUsed': isUsed});
|
||||
'isUsed': isUsed,
|
||||
'silentAddressLabel': silentAddressLabel,
|
||||
'silentPaymentTweak': silentPaymentTweak,
|
||||
'type': type?.toString() ?? '',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,14 +2,19 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
|||
import 'package:cw_core/unspent_transaction_output.dart';
|
||||
|
||||
class BitcoinUnspent extends Unspent {
|
||||
BitcoinUnspent(BitcoinAddressRecord addressRecord, String hash, int value, int vout)
|
||||
BitcoinUnspent(BitcoinAddressRecord addressRecord, String hash, int value, int vout,
|
||||
{this.silentPaymentTweak, bool? isTaproot})
|
||||
: bitcoinAddressRecord = addressRecord,
|
||||
isTaproot = isTaproot ?? false,
|
||||
super(addressRecord.address, hash, value, vout, null);
|
||||
|
||||
factory BitcoinUnspent.fromJSON(
|
||||
BitcoinAddressRecord address, Map<String, dynamic> json) =>
|
||||
BitcoinUnspent(address, json['tx_hash'] as String, json['value'] as int,
|
||||
json['tx_pos'] as int);
|
||||
factory BitcoinUnspent.fromJSON(BitcoinAddressRecord address, Map<String, dynamic> json) =>
|
||||
BitcoinUnspent(
|
||||
address, json['tx_hash'] as String, json['value'] as int, json['tx_pos'] as int,
|
||||
silentPaymentTweak: json['silent_payment_tweak'] as String?,
|
||||
isTaproot: json['is_taproot'] as bool?);
|
||||
|
||||
final BitcoinAddressRecord bitcoinAddressRecord;
|
||||
String? silentPaymentTweak;
|
||||
bool isTaproot = false;
|
||||
}
|
||||
|
|
|
@ -22,53 +22,58 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
bitcoin.NetworkType? networkType,
|
||||
required Uint8List seedBytes,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumBalance? initialBalance,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0})
|
||||
int initialChangeAddressIndex = 0,
|
||||
bitcoin.SilentPaymentReceiver? silentAddress})
|
||||
: super(
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: bitcoin.bitcoin,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: seedBytes,
|
||||
currency: CryptoCurrency.btc) {
|
||||
walletAddresses = BitcoinWalletAddresses(
|
||||
walletInfo,
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: networkType ?? bitcoin.bitcoin,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: seedBytes,
|
||||
currency: CryptoCurrency.btc,
|
||||
) {
|
||||
walletAddresses = BitcoinWalletAddresses(walletInfo,
|
||||
electrumClient: electrumClient,
|
||||
initialAddresses: initialAddresses,
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
||||
.derivePath("m/0'/1"),
|
||||
networkType: networkType);
|
||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
||||
networkType: networkType ?? bitcoin.bitcoin,
|
||||
silentAddress: silentAddress);
|
||||
}
|
||||
|
||||
static Future<BitcoinWallet> create({
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumBalance? initialBalance,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0
|
||||
}) async {
|
||||
static Future<BitcoinWallet> create(
|
||||
{required String mnemonic,
|
||||
required String password,
|
||||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
bitcoin.NetworkType? networkType,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumBalance? initialBalance,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0}) async {
|
||||
return BitcoinWallet(
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: networkType,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance,
|
||||
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex);
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
silentAddress: await bitcoin.SilentPaymentReceiver.fromMnemonic(mnemonic,
|
||||
hrp: networkType == bitcoin.bitcoin ? 'sp' : 'tsp'));
|
||||
}
|
||||
|
||||
static Future<BitcoinWallet> open({
|
||||
|
@ -77,16 +82,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required String password,
|
||||
}) async {
|
||||
final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password);
|
||||
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password);
|
||||
return BitcoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: snp.networkType,
|
||||
initialAddresses: snp.addresses,
|
||||
initialBalance: snp.balance,
|
||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp.changeAddressIndex);
|
||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||
silentAddress: await bitcoin.SilentPaymentReceiver.fromMnemonic(snp.mnemonic,
|
||||
hrp: snp.networkType == bitcoin.bitcoin ? 'sp' : 'tsp'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
required ElectrumClient electrumClient,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0})
|
||||
int initialChangeAddressIndex = 0,
|
||||
bitcoin.SilentPaymentReceiver? silentAddress})
|
||||
: super(walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
|
@ -26,9 +27,18 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
mainHd: mainHd,
|
||||
sideHd: sideHd,
|
||||
electrumClient: electrumClient,
|
||||
networkType: networkType);
|
||||
networkType: networkType,
|
||||
silentAddress: silentAddress);
|
||||
|
||||
@override
|
||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
||||
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
||||
String getAddress(
|
||||
{required int index, required bitcoin.HDWallet hd, bitcoin.AddressType? addressType}) {
|
||||
if (addressType == bitcoin.AddressType.p2pkh)
|
||||
return generateP2PKHAddress(hd: hd, index: index, networkType: networkType);
|
||||
|
||||
if (addressType == bitcoin.AddressType.p2tr)
|
||||
return generateP2TRAddress(hd: hd, index: index, networkType: networkType);
|
||||
|
||||
return generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,10 @@ import 'package:cw_core/wallet_info.dart';
|
|||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
|
||||
class BitcoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials> {
|
||||
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
@ -25,12 +24,14 @@ class BitcoinWalletService extends WalletService<
|
|||
WalletType getType() => WalletType.bitcoin;
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
||||
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final wallet = await BitcoinWalletBase.create(
|
||||
mnemonic: await generateMnemonic(),
|
||||
password: credentials.password!,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
mnemonic: await generateMnemonic(),
|
||||
password: credentials.password!,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
networkType: isTestnet == true ? bitcoin.testnet : bitcoin.bitcoin,
|
||||
);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
|
@ -42,33 +43,36 @@ class BitcoinWalletService extends WalletService<
|
|||
|
||||
@override
|
||||
Future<BitcoinWallet> openWallet(String name, String password) async {
|
||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||
final walletInfo = walletInfoSource.values
|
||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||
final wallet = await BitcoinWalletBase.open(
|
||||
password: password, name: name, walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
password: password,
|
||||
name: name,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
);
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> remove(String wallet) async {
|
||||
File(await pathForWalletDir(name: wallet, type: getType()))
|
||||
.delete(recursive: true);
|
||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
|
||||
final walletInfo = walletInfoSource.values
|
||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||
await walletInfoSource.delete(walletInfo.key);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> rename(String currentName, String password, String newName) async {
|
||||
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||
final currentWalletInfo = walletInfoSource.values
|
||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||
final currentWallet = await BitcoinWalletBase.open(
|
||||
password: password,
|
||||
name: currentName,
|
||||
walletInfo: currentWalletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
password: password,
|
||||
name: currentName,
|
||||
walletInfo: currentWalletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
);
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
|
||||
|
@ -80,24 +84,23 @@ class BitcoinWalletService extends WalletService<
|
|||
}
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromKeys(
|
||||
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
||||
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromSeed(
|
||||
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||
if (!validateMnemonic(credentials.mnemonic)) {
|
||||
throw BitcoinMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
final wallet = await BitcoinWalletBase.create(
|
||||
password: credentials.password!,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
password: credentials.password!,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:cw_bitcoin/script_hash.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
String jsonrpcparams(List<Object> params) {
|
||||
final _params = params?.map((val) => '"${val.toString()}"')?.join(',');
|
||||
|
@ -22,10 +23,7 @@ String jsonrpc(
|
|||
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
|
||||
|
||||
class SocketTask {
|
||||
SocketTask({
|
||||
required this.isSubscription,
|
||||
this.completer,
|
||||
this.subject});
|
||||
SocketTask({required this.isSubscription, this.completer, this.subject});
|
||||
|
||||
final Completer<dynamic>? completer;
|
||||
final BehaviorSubject<dynamic>? subject;
|
||||
|
@ -51,8 +49,12 @@ class ElectrumClient {
|
|||
Timer? _aliveTimer;
|
||||
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);
|
||||
}
|
||||
|
||||
Future<void> connect({required String host, required int port}) async {
|
||||
try {
|
||||
|
@ -104,21 +106,20 @@ class ElectrumClient {
|
|||
}
|
||||
|
||||
if (isJSONStringCorrect(unterminatedString)) {
|
||||
final response =
|
||||
json.decode(unterminatedString) as Map<String, dynamic>;
|
||||
final response = json.decode(unterminatedString) as Map<String, dynamic>;
|
||||
_handleResponse(response);
|
||||
unterminatedString = '';
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
unterminatedString += message;
|
||||
|
||||
if (isJSONStringCorrect(unterminatedString)) {
|
||||
final response =
|
||||
json.decode(unterminatedString) as Map<String, dynamic>;
|
||||
final response = json.decode(unterminatedString) as Map<String, dynamic>;
|
||||
_handleResponse(response);
|
||||
// unterminatedString = null;
|
||||
unterminatedString = '';
|
||||
|
@ -142,8 +143,7 @@ class ElectrumClient {
|
|||
}
|
||||
}
|
||||
|
||||
Future<List<String>> version() =>
|
||||
call(method: 'server.version').then((dynamic result) {
|
||||
Future<List<String>> version() => call(method: 'server.version').then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) => val.toString()).toList();
|
||||
}
|
||||
|
@ -180,9 +180,8 @@ class ElectrumClient {
|
|||
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
||||
String address, NetworkType networkType) =>
|
||||
call(
|
||||
method: 'blockchain.scripthash.listunspent',
|
||||
params: [scriptHash(address, networkType: networkType)])
|
||||
.then((dynamic result) {
|
||||
method: 'blockchain.scripthash.listunspent',
|
||||
params: [scriptHash(address, networkType: networkType)]).then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, dynamic>) {
|
||||
|
@ -229,19 +228,25 @@ class ElectrumClient {
|
|||
return [];
|
||||
});
|
||||
|
||||
Future<Map<String, dynamic>> getTransactionRaw(
|
||||
{required String hash}) async =>
|
||||
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000)
|
||||
Future<dynamic> getTransactionRaw(
|
||||
{required String hash, required NetworkType networkType}) async =>
|
||||
callWithTimeout(
|
||||
method: 'blockchain.transaction.get',
|
||||
params: networkType.bech32 == bitcoin.bech32 ? [hash, true] : [hash],
|
||||
timeout: 10000)
|
||||
.then((dynamic result) {
|
||||
if (result is Map<String, dynamic>) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (networkType.bech32 == testnet.bech32 && result is String) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return <String, dynamic>{};
|
||||
});
|
||||
|
||||
Future<String> getTransactionHex(
|
||||
{required String hash}) async =>
|
||||
Future<String> getTransactionHex({required String hash}) async =>
|
||||
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000)
|
||||
.then((dynamic result) {
|
||||
if (result is String) {
|
||||
|
@ -251,30 +256,41 @@ class ElectrumClient {
|
|||
return '';
|
||||
});
|
||||
|
||||
Future<String> broadcastTransaction(
|
||||
{required String transactionRaw}) async =>
|
||||
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
||||
.then((dynamic result) {
|
||||
if (result is String) {
|
||||
return result;
|
||||
}
|
||||
Future<String> broadcastTransaction({required String transactionRaw}) async {
|
||||
return http
|
||||
.post(Uri(scheme: 'https', host: 'blockstream.info', path: '/testnet/api/tx'),
|
||||
headers: <String, String>{'Content-Type': 'application/json; charset=utf-8'},
|
||||
body: transactionRaw)
|
||||
.then((http.Response response) {
|
||||
print(response.body);
|
||||
if (response.statusCode == 200) {
|
||||
return response.body;
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
return '';
|
||||
});
|
||||
return call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
||||
.then((dynamic result) {
|
||||
if (result is String) {
|
||||
return result;
|
||||
}
|
||||
|
||||
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>;
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getHeader({required int height}) async =>
|
||||
await call(method: 'blockchain.block.get_header', params: [height])
|
||||
Future<String> getTxidFromPos({required int height, required int pos}) async =>
|
||||
await call(method: 'blockchain.transaction.id_from_pos', params: [height, pos]) as String;
|
||||
|
||||
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>;
|
||||
|
||||
Future<Map<String, dynamic>> getHeader({required int height}) async =>
|
||||
await call(method: 'blockchain.block.get_header', params: [height]) as Map<String, dynamic>;
|
||||
|
||||
Future<double> estimatefee({required int p}) =>
|
||||
call(method: 'blockchain.estimatefee', params: [p])
|
||||
.then((dynamic result) {
|
||||
call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) {
|
||||
if (result is double) {
|
||||
return result;
|
||||
}
|
||||
|
@ -319,15 +335,9 @@ class ElectrumClient {
|
|||
final topDoubleString = await estimatefee(p: 1);
|
||||
final middleDoubleString = await estimatefee(p: 5);
|
||||
final bottomDoubleString = await estimatefee(p: 100);
|
||||
final top =
|
||||
(stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000)
|
||||
.round();
|
||||
final middle =
|
||||
(stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000)
|
||||
.round();
|
||||
final bottom =
|
||||
(stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000)
|
||||
.round();
|
||||
final top = (stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000).round();
|
||||
final middle = (stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000).round();
|
||||
final bottom = (stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000).round();
|
||||
|
||||
return [bottom, middle, top];
|
||||
} catch (_) {
|
||||
|
@ -335,6 +345,27 @@ class ElectrumClient {
|
|||
}
|
||||
}
|
||||
|
||||
// https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-headers-subscribe
|
||||
// example response:
|
||||
// {
|
||||
// "height": 520481,
|
||||
// "hex": "00000020890208a0ae3a3892aa047c5468725846577cfcd9b512b50000000000000000005dc2b02f2d297a9064ee103036c14d678f9afc7e3d9409cf53fd58b82e938e8ecbeca05a2d2103188ce804c4"
|
||||
// }
|
||||
Future<int?> getCurrentBlockChainTip() =>
|
||||
call(method: 'blockchain.headers.subscribe').then((result) {
|
||||
if (result is Map<String, dynamic>) {
|
||||
return result["height"] as int;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
BehaviorSubject<Object>? chainTipUpdate() {
|
||||
_id += 1;
|
||||
return subscribe<Object>(
|
||||
id: 'blockchain.headers.subscribe', method: 'blockchain.headers.subscribe');
|
||||
}
|
||||
|
||||
BehaviorSubject<Object>? scripthashUpdate(String scripthash) {
|
||||
_id += 1;
|
||||
return subscribe<Object>(
|
||||
|
@ -344,16 +375,14 @@ class ElectrumClient {
|
|||
}
|
||||
|
||||
BehaviorSubject<T>? subscribe<T>(
|
||||
{required String id,
|
||||
required String method,
|
||||
List<Object> params = const []}) {
|
||||
{required String id, required String method, List<Object> params = const []}) {
|
||||
try {
|
||||
final subscription = BehaviorSubject<T>();
|
||||
_regisrySubscription(id, subscription);
|
||||
socket!.write(jsonrpc(method: method, id: _id, params: params));
|
||||
|
||||
return subscription;
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
return null;
|
||||
}
|
||||
|
@ -370,9 +399,7 @@ class ElectrumClient {
|
|||
}
|
||||
|
||||
Future<dynamic> callWithTimeout(
|
||||
{required String method,
|
||||
List<Object> params = const [],
|
||||
int timeout = 4000}) async {
|
||||
{required String method, List<Object> params = const [], int timeout = 4000}) async {
|
||||
try {
|
||||
final completer = Completer<dynamic>();
|
||||
_id += 1;
|
||||
|
@ -386,7 +413,7 @@ class ElectrumClient {
|
|||
});
|
||||
|
||||
return completer.future;
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
@ -397,8 +424,8 @@ class ElectrumClient {
|
|||
onConnectionStatusChange = null;
|
||||
}
|
||||
|
||||
void _registryTask(int id, Completer<dynamic> completer) => _tasks[id.toString()] =
|
||||
SocketTask(completer: completer, isSubscription: false);
|
||||
void _registryTask(int id, Completer<dynamic> completer) =>
|
||||
_tasks[id.toString()] = SocketTask(completer: completer, isSubscription: false);
|
||||
|
||||
void _regisrySubscription(String id, BehaviorSubject<dynamic> subject) =>
|
||||
_tasks[id] = SocketTask(subject: subject, isSubscription: true);
|
||||
|
@ -419,8 +446,7 @@ class ElectrumClient {
|
|||
}
|
||||
}
|
||||
|
||||
void _methodHandler(
|
||||
{required String method, required Map<String, dynamic> request}) {
|
||||
void _methodHandler({required String method, required Map<String, dynamic> request}) {
|
||||
switch (method) {
|
||||
case 'blockchain.scripthash.subscribe':
|
||||
final params = request['params'] as List<dynamic>;
|
||||
|
@ -451,8 +477,8 @@ class ElectrumClient {
|
|||
_methodHandler(method: method, request: response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (id != null){
|
||||
|
||||
if (id != null) {
|
||||
_finish(id, result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,7 @@ import 'package:cw_core/wallet_type.dart';
|
|||
|
||||
class ElectrumTransactionBundle {
|
||||
ElectrumTransactionBundle(this.originalTransaction,
|
||||
{required this.ins,
|
||||
required this.confirmations,
|
||||
this.time});
|
||||
{required this.ins, required this.confirmations, this.time});
|
||||
final bitcoin.Transaction originalTransaction;
|
||||
final List<bitcoin.Transaction> ins;
|
||||
final int? time;
|
||||
|
@ -39,8 +37,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
this.confirmations = confirmations;
|
||||
}
|
||||
|
||||
factory ElectrumTransactionInfo.fromElectrumVerbose(
|
||||
Map<String, Object> obj, WalletType type,
|
||||
factory ElectrumTransactionInfo.fromElectrumVerbose(Map<String, Object> obj, WalletType type,
|
||||
{required List<BitcoinAddressRecord> addresses, required int height}) {
|
||||
final addressesSet = addresses.map((addr) => addr.address).toSet();
|
||||
final id = obj['txid'] as String;
|
||||
|
@ -58,10 +55,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
for (dynamic vin in vins) {
|
||||
final vout = vin['vout'] as int;
|
||||
final out = vin['tx']['vout'][vout] as Map;
|
||||
final outAddresses =
|
||||
(out['scriptPubKey']['addresses'] as List<Object>?)?.toSet();
|
||||
inputsAmount +=
|
||||
stringDoubleToBitcoinAmount((out['value'] as double? ?? 0).toString());
|
||||
final outAddresses = (out['scriptPubKey']['addresses'] as List<Object>?)?.toSet();
|
||||
inputsAmount += stringDoubleToBitcoinAmount((out['value'] as double? ?? 0).toString());
|
||||
|
||||
if (outAddresses?.intersection(addressesSet).isNotEmpty ?? false) {
|
||||
direction = TransactionDirection.outgoing;
|
||||
|
@ -69,11 +64,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
}
|
||||
|
||||
for (dynamic out in vout) {
|
||||
final outAddresses =
|
||||
out['scriptPubKey']['addresses'] as List<Object>? ?? [];
|
||||
final outAddresses = out['scriptPubKey']['addresses'] as List<Object>? ?? [];
|
||||
final ntrs = outAddresses.toSet().intersection(addressesSet);
|
||||
final value = stringDoubleToBitcoinAmount(
|
||||
(out['value'] as double? ?? 0.0).toString());
|
||||
final value = stringDoubleToBitcoinAmount((out['value'] as double? ?? 0.0).toString());
|
||||
totalOutAmount += value;
|
||||
|
||||
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||
|
||||
|
@ -96,14 +89,11 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
}
|
||||
|
||||
factory ElectrumTransactionInfo.fromElectrumBundle(
|
||||
ElectrumTransactionBundle bundle,
|
||||
WalletType type,
|
||||
bitcoin.NetworkType networkType,
|
||||
{required Set<String> addresses,
|
||||
required int height}) {
|
||||
ElectrumTransactionBundle bundle, WalletType type, bitcoin.NetworkType networkType,
|
||||
{required Set<String> addresses, required int height}) {
|
||||
final date = bundle.time != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(bundle.time! * 1000)
|
||||
: DateTime.now();
|
||||
? DateTime.fromMillisecondsSinceEpoch(bundle.time! * 1000)
|
||||
: DateTime.now();
|
||||
var direction = TransactionDirection.incoming;
|
||||
var amount = 0;
|
||||
var inputAmount = 0;
|
||||
|
@ -152,8 +142,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
if (addresses != null) {
|
||||
tx.outs.forEach((out) {
|
||||
try {
|
||||
final p2pkh = bitcoin.P2PKH(
|
||||
data: PaymentData(output: out.script), network: bitcoin.bitcoin);
|
||||
final p2pkh =
|
||||
bitcoin.P2PKH(data: PaymentData(output: out.script), network: bitcoin.bitcoin);
|
||||
exist = addresses.contains(p2pkh.data.address);
|
||||
|
||||
if (exist) {
|
||||
|
@ -163,9 +153,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
});
|
||||
}
|
||||
|
||||
final date = timestamp != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000)
|
||||
: DateTime.now();
|
||||
final date =
|
||||
timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000) : DateTime.now();
|
||||
|
||||
return ElectrumTransactionInfo(type,
|
||||
id: tx.getId(),
|
||||
|
@ -178,8 +167,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
|||
confirmations: confirmations);
|
||||
}
|
||||
|
||||
factory ElectrumTransactionInfo.fromJson(
|
||||
Map<String, dynamic> data, WalletType type) {
|
||||
factory ElectrumTransactionInfo.fromJson(Map<String, dynamic> data, WalletType type) {
|
||||
return ElectrumTransactionInfo(type,
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -10,31 +10,33 @@ import 'package:mobx/mobx.dart';
|
|||
|
||||
part 'electrum_wallet_addresses.g.dart';
|
||||
|
||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase
|
||||
with _$ElectrumWalletAddresses;
|
||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
||||
|
||||
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
ElectrumWalletAddressesBase(WalletInfo walletInfo,
|
||||
{required this.mainHd,
|
||||
required this.sideHd,
|
||||
required this.electrumClient,
|
||||
required this.networkType,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0})
|
||||
: addresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? []).toSet()),
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? [])
|
||||
ElectrumWalletAddressesBase(
|
||||
WalletInfo walletInfo, {
|
||||
required this.mainHd,
|
||||
required this.sideHd,
|
||||
required this.electrumClient,
|
||||
required this.networkType,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0,
|
||||
bitcoin.SilentPaymentReceiver? silentAddress,
|
||||
}) : addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
|
||||
silentAddress = silentAddress,
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
|
||||
.toSet()),
|
||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? [])
|
||||
.toSet()),
|
||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
|
||||
.toSet()),
|
||||
.toSet()),
|
||||
silentAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||
.where((addressRecord) => addressRecord.silentAddressLabel != null)
|
||||
.toSet()),
|
||||
currentReceiveAddressIndex = initialRegularAddressIndex,
|
||||
currentChangeAddressIndex = initialChangeAddressIndex,
|
||||
super(walletInfo);
|
||||
super(walletInfo);
|
||||
|
||||
static const defaultReceiveAddressesCount = 22;
|
||||
static const defaultChangeAddressesCount = 17;
|
||||
|
@ -45,14 +47,26 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
final ObservableList<BitcoinAddressRecord> addresses;
|
||||
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
||||
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
||||
final ObservableList<BitcoinAddressRecord> silentAddresses;
|
||||
final ElectrumClient electrumClient;
|
||||
final bitcoin.NetworkType networkType;
|
||||
final bitcoin.HDWallet mainHd;
|
||||
final bitcoin.HDWallet sideHd;
|
||||
|
||||
@override
|
||||
// TODO: labels -> disable edit on receive page
|
||||
final bitcoin.SilentPaymentReceiver? silentAddress;
|
||||
|
||||
@observable
|
||||
// ignore: prefer_final_fields
|
||||
dynamic _addressPageType = bitcoin.AddressType.p2wpkh;
|
||||
@computed
|
||||
String get address {
|
||||
dynamic get addressPageType => _addressPageType;
|
||||
|
||||
@observable
|
||||
String? activeAddress;
|
||||
|
||||
@computed
|
||||
String get receiveAddress {
|
||||
if (receiveAddresses.isEmpty) {
|
||||
final address = generateNewAddress().address;
|
||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(address) : address;
|
||||
|
@ -63,28 +77,54 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
@override
|
||||
set address(String addr) => null;
|
||||
@computed
|
||||
String get address {
|
||||
if (addressPageType == bitcoin.AddressType.p2sp) {
|
||||
return silentAddress!.toString();
|
||||
}
|
||||
|
||||
if (activeAddress != null) {
|
||||
return activeAddress!;
|
||||
}
|
||||
|
||||
if (receiveAddresses.isEmpty) {
|
||||
return generateNewAddress().address;
|
||||
}
|
||||
|
||||
try {
|
||||
receiveAddresses.forEach(
|
||||
(element) => print('element: ${element.address} ${element.type} $addressPageType'));
|
||||
return receiveAddresses
|
||||
.firstWhere((address) => addressPageType == bitcoin.AddressType.p2wpkh
|
||||
? address.type == null || address.type == addressPageType
|
||||
: address.type == addressPageType)
|
||||
.address;
|
||||
} catch (_) {}
|
||||
|
||||
return receiveAddresses.first.address;
|
||||
}
|
||||
|
||||
@override
|
||||
set address(String addr) => activeAddress = addr;
|
||||
|
||||
int currentReceiveAddressIndex;
|
||||
int currentChangeAddressIndex;
|
||||
|
||||
@computed
|
||||
int get totalCountOfReceiveAddresses =>
|
||||
addresses.fold(0, (acc, addressRecord) {
|
||||
if (!addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
int get totalCountOfReceiveAddresses => addresses.fold(0, (acc, addressRecord) {
|
||||
if (!addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
|
||||
@computed
|
||||
int get totalCountOfChangeAddresses =>
|
||||
addresses.fold(0, (acc, addressRecord) {
|
||||
if (addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
int get totalCountOfChangeAddresses => addresses.fold(0, (acc, addressRecord) {
|
||||
if (addressRecord.isHidden) {
|
||||
return acc + 1;
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
|
||||
Future<void> discoverAddresses() async {
|
||||
await _discoverAddresses(mainHd, false);
|
||||
|
@ -114,12 +154,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
if (changeAddresses.isEmpty) {
|
||||
final newAddresses = await _createNewAddresses(gap,
|
||||
hd: sideHd,
|
||||
startIndex: totalCountOfChangeAddresses > 0
|
||||
? totalCountOfChangeAddresses - 1
|
||||
: 0,
|
||||
isHidden: true);
|
||||
_addAddresses(newAddresses);
|
||||
hd: sideHd,
|
||||
startIndex: totalCountOfChangeAddresses > 0 ? totalCountOfChangeAddresses - 1 : 0,
|
||||
isHidden: true);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
if (currentChangeAddressIndex >= changeAddresses.length) {
|
||||
|
@ -132,19 +170,44 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return address;
|
||||
}
|
||||
|
||||
@action
|
||||
BitcoinAddressRecord generateNewAddress(
|
||||
{bitcoin.HDWallet? hd, bool isHidden = false}) {
|
||||
currentReceiveAddressIndex += 1;
|
||||
{bitcoin.HDWallet? hd, bool isHidden = false, String? label}) {
|
||||
if (label != null && silentAddress != null) {
|
||||
final address = BitcoinAddressRecord(
|
||||
bitcoin.SilentPaymentAddress.createLabeledSilentPaymentAddress(
|
||||
silentAddress!.scanPubkey,
|
||||
silentAddress!.spendPubkey,
|
||||
'0000000000000000000000000000000000000000000000000000000000000002'.fromHex,
|
||||
hrp: silentAddress!.hrp,
|
||||
version: silentAddress!.version)
|
||||
.toString(),
|
||||
index: currentReceiveAddressIndex,
|
||||
isHidden: isHidden,
|
||||
silentAddressLabel: label);
|
||||
silentAddresses.add(address);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
// FIX-ME: Check logic for whichi HD should be used here ???
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(index: currentReceiveAddressIndex, hd: hd ?? sideHd),
|
||||
getAddress(
|
||||
index: currentReceiveAddressIndex,
|
||||
hd: hd ?? sideHd,
|
||||
addressType: addressPageType as bitcoin.AddressType,
|
||||
),
|
||||
index: currentReceiveAddressIndex,
|
||||
isHidden: isHidden);
|
||||
addresses.add(address);
|
||||
return address;
|
||||
|
||||
currentReceiveAddressIndex += 1;
|
||||
}
|
||||
|
||||
String getAddress({required int index, required bitcoin.HDWallet hd}) => '';
|
||||
String getAddress(
|
||||
{required int index, required bitcoin.HDWallet hd, bitcoin.AddressType? addressType}) =>
|
||||
'';
|
||||
|
||||
@override
|
||||
Future<void> updateAddressesInBox() async {
|
||||
|
@ -160,38 +223,37 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@action
|
||||
void updateReceiveAddresses() {
|
||||
receiveAddresses.removeRange(0, receiveAddresses.length);
|
||||
final newAdresses = addresses
|
||||
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||
final newAdresses =
|
||||
addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||
receiveAddresses.addAll(newAdresses);
|
||||
}
|
||||
|
||||
@action
|
||||
void updateChangeAddresses() {
|
||||
changeAddresses.removeRange(0, changeAddresses.length);
|
||||
final newAdresses = addresses
|
||||
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
|
||||
final newAdresses =
|
||||
addresses.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
|
||||
changeAddresses.addAll(newAdresses);
|
||||
}
|
||||
|
||||
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
|
||||
@action
|
||||
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden,
|
||||
{bitcoin.AddressType? addressType}) async {
|
||||
var hasAddrUse = true;
|
||||
List<BitcoinAddressRecord> addrs;
|
||||
|
||||
if (addresses.isNotEmpty) {
|
||||
addrs = addresses
|
||||
.where((addr) => addr.isHidden == isHidden)
|
||||
.toList();
|
||||
if (addresses.where((addr) => addr.type == addressPageType).isNotEmpty) {
|
||||
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
||||
} else {
|
||||
addrs = await _createNewAddresses(
|
||||
isHidden
|
||||
? defaultChangeAddressesCount
|
||||
: defaultReceiveAddressesCount,
|
||||
isHidden ? defaultChangeAddressesCount : defaultReceiveAddressesCount,
|
||||
startIndex: 0,
|
||||
hd: hd,
|
||||
isHidden: isHidden);
|
||||
isHidden: isHidden,
|
||||
addressType: addressType);
|
||||
}
|
||||
|
||||
while(hasAddrUse) {
|
||||
while (hasAddrUse) {
|
||||
final addr = addrs.last.address;
|
||||
hasAddrUse = await _hasAddressUsed(addr);
|
||||
|
||||
|
@ -201,16 +263,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
final start = addrs.length;
|
||||
final count = start + gap;
|
||||
final batch = await _createNewAddresses(
|
||||
count,
|
||||
startIndex: start,
|
||||
hd: hd,
|
||||
isHidden: isHidden);
|
||||
final batch = await _createNewAddresses(count,
|
||||
startIndex: start, hd: hd, isHidden: isHidden, addressType: addressType);
|
||||
addrs.addAll(batch);
|
||||
}
|
||||
|
||||
if (addresses.length < addrs.length) {
|
||||
_addAddresses(addrs);
|
||||
if (addresses.length < addrs.length || addressPageType != null) {
|
||||
addAddresses(addrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,45 +288,41 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||
final newAddresses = await _createNewAddresses(
|
||||
addressesCount,
|
||||
startIndex: countOfReceiveAddresses,
|
||||
hd: mainHd,
|
||||
isHidden: false);
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfReceiveAddresses, hd: mainHd, isHidden: false);
|
||||
addresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(
|
||||
addressesCount,
|
||||
startIndex: countOfHiddenAddresses,
|
||||
hd: sideHd,
|
||||
isHidden: true);
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses, hd: sideHd, isHidden: true);
|
||||
addresses.addAll(newAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||
{required bitcoin.HDWallet hd, int startIndex = 0, bool isHidden = false}) async {
|
||||
{required bitcoin.HDWallet hd,
|
||||
int startIndex = 0,
|
||||
bool isHidden = false,
|
||||
bitcoin.AddressType? addressType}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(index: i, hd: hd),
|
||||
index: i,
|
||||
isHidden: isHidden);
|
||||
final address = BitcoinAddressRecord(getAddress(index: i, hd: hd, addressType: addressType),
|
||||
index: i, isHidden: isHidden, type: addressType);
|
||||
list.add(address);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void _addAddresses(Iterable<BitcoinAddressRecord> addresses) {
|
||||
@action
|
||||
void addAddresses(Iterable<BitcoinAddressRecord> addresses) {
|
||||
final addressesSet = this.addresses.toSet();
|
||||
addressesSet.addAll(addresses);
|
||||
this.addresses.removeRange(0, this.addresses.length);
|
||||
this.addresses.addAll(addressesSet);
|
||||
this.addresses.addAll(addresses);
|
||||
}
|
||||
|
||||
Future<bool> _hasAddressUsed(String address) async {
|
||||
|
@ -275,4 +330,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
final transactionHistory = await electrumClient.getHistory(sh);
|
||||
return transactionHistory.isNotEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<void> setAddressType(dynamic type) async {
|
||||
_addressPageType = type as bitcoin.AddressType;
|
||||
|
||||
if (addressPageType != bitcoin.AddressType.p2sp) {
|
||||
await _discoverAddresses(mainHd, false, addressType: addressPageType as bitcoin.AddressType);
|
||||
updateReceiveAddresses();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,20 @@ import 'package:cw_bitcoin/electrum_balance.dart';
|
|||
import 'package:cw_bitcoin/file.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
|
||||
class ElectrumWallletSnapshot {
|
||||
ElectrumWallletSnapshot({
|
||||
class ElectrumWalletSnapshot {
|
||||
ElectrumWalletSnapshot({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.password,
|
||||
required this.mnemonic,
|
||||
required this.addresses,
|
||||
required this.balance,
|
||||
required this.networkType,
|
||||
required this.regularAddressIndex,
|
||||
required this.changeAddressIndex});
|
||||
required this.changeAddressIndex,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String password;
|
||||
|
@ -23,10 +26,11 @@ class ElectrumWallletSnapshot {
|
|||
String mnemonic;
|
||||
List<BitcoinAddressRecord> addresses;
|
||||
ElectrumBalance balance;
|
||||
bitcoin.NetworkType networkType;
|
||||
int regularAddressIndex;
|
||||
int changeAddressIndex;
|
||||
|
||||
static Future<ElectrumWallletSnapshot> load(String name, WalletType type, String password) async {
|
||||
static Future<ElectrumWalletSnapshot> load(String name, WalletType type, String password) async {
|
||||
final path = await pathForWallet(name: name, type: type);
|
||||
final jsonSource = await read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
|
@ -38,6 +42,7 @@ class ElectrumWallletSnapshot {
|
|||
.toList();
|
||||
final balance = ElectrumBalance.fromJSON(data['balance'] as String) ??
|
||||
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
||||
final networkType = data['network_type'] == 'testnet' ? bitcoin.testnet : bitcoin.bitcoin;
|
||||
var regularAddressIndex = 0;
|
||||
var changeAddressIndex = 0;
|
||||
|
||||
|
@ -46,14 +51,15 @@ class ElectrumWallletSnapshot {
|
|||
changeAddressIndex = int.parse(data['change_address_index'] as String? ?? '0');
|
||||
} catch (_) {}
|
||||
|
||||
return ElectrumWallletSnapshot(
|
||||
name: name,
|
||||
type: type,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
addresses: addresses,
|
||||
balance: balance,
|
||||
regularAddressIndex: regularAddressIndex,
|
||||
changeAddressIndex: changeAddressIndex);
|
||||
return ElectrumWalletSnapshot(
|
||||
name: name,
|
||||
type: type,
|
||||
password: password,
|
||||
mnemonic: mnemonic,
|
||||
addresses: addresses,
|
||||
balance: balance,
|
||||
networkType: networkType,
|
||||
regularAddressIndex: regularAddressIndex,
|
||||
changeAddressIndex: changeAddressIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
|
||||
final litecoinNetwork = NetworkType(
|
||||
messagePrefix: '\x19Litecoin Signed Message:\n',
|
||||
bech32: 'ltc',
|
||||
bip32: Bip32Type(public: 0x0488b21e, private: 0x0488ade4),
|
||||
pubKeyHash: 0x30,
|
||||
scriptHash: 0x32,
|
||||
wif: 0xb0);
|
||||
messagePrefix: '\x19Litecoin Signed Message:\n',
|
||||
bech32: 'ltc',
|
||||
bip32: Bip32Type(public: 0x0488b21e, private: 0x0488ade4),
|
||||
pubKeyHash: 0x30,
|
||||
scriptHash: 0x32,
|
||||
wif: 0xb0,
|
||||
p2pkhPrefix: bitcoin.p2pkhPrefix,
|
||||
network: bitcoin.network,
|
||||
p2shPrefix: bitcoin.p2shPrefix,
|
||||
extendPublic: bitcoin.extendPublic,
|
||||
extendPrivate: bitcoin.extendPrivate,
|
||||
);
|
||||
|
|
|
@ -81,7 +81,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required String password,
|
||||
}) async {
|
||||
final snp = await ElectrumWallletSnapshot.load (name, walletInfo.type, password);
|
||||
final snp = await ElectrumWalletSnapshot.load (name, walletInfo.type, password);
|
||||
return LitecoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
password: password,
|
||||
|
|
|
@ -9,31 +9,29 @@ import 'package:mobx/mobx.dart';
|
|||
|
||||
part 'litecoin_wallet_addresses.g.dart';
|
||||
|
||||
class LitecoinWalletAddresses = LitecoinWalletAddressesBase
|
||||
with _$LitecoinWalletAddresses;
|
||||
class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses;
|
||||
|
||||
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
|
||||
with Store {
|
||||
LitecoinWalletAddressesBase(
|
||||
WalletInfo walletInfo,
|
||||
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
||||
LitecoinWalletAddressesBase(WalletInfo walletInfo,
|
||||
{required bitcoin.HDWallet mainHd,
|
||||
required bitcoin.HDWallet sideHd,
|
||||
required bitcoin.NetworkType networkType,
|
||||
required ElectrumClient electrumClient,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0})
|
||||
: super(
|
||||
walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: mainHd,
|
||||
sideHd: sideHd,
|
||||
electrumClient: electrumClient,
|
||||
networkType: networkType);
|
||||
required bitcoin.HDWallet sideHd,
|
||||
required bitcoin.NetworkType networkType,
|
||||
required ElectrumClient electrumClient,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
int initialRegularAddressIndex = 0,
|
||||
int initialChangeAddressIndex = 0})
|
||||
: super(walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||
mainHd: mainHd,
|
||||
sideHd: sideHd,
|
||||
electrumClient: electrumClient,
|
||||
networkType: networkType);
|
||||
|
||||
@override
|
||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
||||
String getAddress(
|
||||
{required int index, required bitcoin.HDWallet hd, bitcoin.AddressType? addressType}) =>
|
||||
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ class LitecoinWalletService extends WalletService<
|
|||
WalletType getType() => WalletType.litecoin;
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
||||
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
final wallet = await LitecoinWalletBase.create(
|
||||
mnemonic: await generateMnemonic(),
|
||||
password: credentials.password!,
|
||||
|
|
|
@ -9,9 +9,7 @@ import 'package:cw_core/wallet_type.dart';
|
|||
|
||||
class PendingBitcoinTransaction with PendingTransaction {
|
||||
PendingBitcoinTransaction(this._tx, this.type,
|
||||
{required this.electrumClient,
|
||||
required this.amount,
|
||||
required this.fee})
|
||||
{required this.electrumClient, required this.amount, required this.fee})
|
||||
: _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
|
||||
|
||||
final WalletType type;
|
||||
|
@ -37,7 +35,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
@override
|
||||
Future<void> commit() async {
|
||||
final result =
|
||||
await electrumClient.broadcastTransaction(transactionRaw: _tx.toHex());
|
||||
await electrumClient.broadcastTransaction(transactionRaw: _tx.txHex ?? _tx.toHex());
|
||||
|
||||
if (result.isEmpty) {
|
||||
throw BitcoinCommitTransactionException();
|
||||
|
@ -46,8 +44,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
|||
_listeners?.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
||||
void addListener(
|
||||
void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
void addListener(void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||
|
|
|
@ -1,9 +1,29 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
String scriptHash(String address, {required bitcoin.NetworkType networkType}) {
|
||||
final outputScript =
|
||||
bitcoin.Address.addressToOutputScript(address, networkType);
|
||||
try {
|
||||
final outputScript = bitcoin.Address.addressToOutputScript(address, networkType);
|
||||
final parts = sha256.convert(outputScript).toString().split('');
|
||||
var res = '';
|
||||
|
||||
for (var i = parts.length - 1; i >= 0; i--) {
|
||||
final char = parts[i];
|
||||
i--;
|
||||
final nextChar = parts[i];
|
||||
res += nextChar;
|
||||
res += char;
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch (e) {}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
String scriptHashFromScript(Uint8List outputScript, {required bitcoin.NetworkType networkType}) {
|
||||
final parts = sha256.convert(outputScript).toString().split('');
|
||||
var res = '';
|
||||
|
||||
|
|
|
@ -4,15 +4,11 @@ import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
|||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||
import 'package:hex/hex.dart';
|
||||
|
||||
bitcoin.PaymentData generatePaymentData(
|
||||
{required bitcoin.HDWallet hd, required int index}) =>
|
||||
PaymentData(
|
||||
pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!)));
|
||||
bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, required int index}) =>
|
||||
PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!)));
|
||||
|
||||
bitcoin.ECPair generateKeyPair(
|
||||
{required bitcoin.HDWallet hd,
|
||||
required int index,
|
||||
required bitcoin.NetworkType network}) =>
|
||||
{required bitcoin.HDWallet hd, required int index, required bitcoin.NetworkType network}) =>
|
||||
bitcoin.ECPair.fromWIF(hd.derive(index).wif!, network: network);
|
||||
|
||||
String generateP2WPKHAddress(
|
||||
|
@ -28,28 +24,19 @@ String generateP2WPKHAddress(
|
|||
.data
|
||||
.address!;
|
||||
|
||||
String generateP2WPKHAddressByPath(
|
||||
{required bitcoin.HDWallet hd,
|
||||
required String path,
|
||||
required bitcoin.NetworkType networkType}) =>
|
||||
bitcoin
|
||||
.P2WPKH(
|
||||
data: PaymentData(
|
||||
pubkey:
|
||||
Uint8List.fromList(HEX.decode(hd.derivePath(path).pubKey!))),
|
||||
network: networkType)
|
||||
.data
|
||||
.address!;
|
||||
|
||||
String generateP2PKHAddress(
|
||||
{required bitcoin.HDWallet hd,
|
||||
required int index,
|
||||
required bitcoin.NetworkType networkType}) =>
|
||||
bitcoin
|
||||
.P2PKH(
|
||||
data: PaymentData(
|
||||
pubkey:
|
||||
Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!))),
|
||||
data: PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!))),
|
||||
network: networkType)
|
||||
.data
|
||||
.address!;
|
||||
|
||||
String generateP2TRAddress(
|
||||
{required bitcoin.HDWallet hd,
|
||||
required int index,
|
||||
required bitcoin.NetworkType networkType}) =>
|
||||
bitcoin.P2trAddress(program: hd.derive(index).pubKey!).toAddress(networkType);
|
||||
|
|
|
@ -21,18 +21,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
version: "2.4.2"
|
||||
asn1lib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: asn1lib
|
||||
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039
|
||||
sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.5.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -45,9 +45,9 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
path: "."
|
||||
ref: "cake-0.2.2"
|
||||
resolved-ref: "05755063b593aa6cca0a4820a318e0ce17de6192"
|
||||
url: "https://github.com/cake-tech/bech32.git"
|
||||
ref: bech32m
|
||||
resolved-ref: "55d966503310fc46bf349e2f56fe1bb513ed429b"
|
||||
url: "https://github.com/saltrafael/bech32.git"
|
||||
source: git
|
||||
version: "0.2.2"
|
||||
bip32:
|
||||
|
@ -75,12 +75,20 @@ packages:
|
|||
url: "https://github.com/cake-tech/bitbox-flutter.git"
|
||||
source: git
|
||||
version: "1.0.1"
|
||||
bitcoin_base:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bitcoin_base
|
||||
sha256: f744eca882f501108639946e1172ab0b2e5553169dffc973cd0bfa78f25986d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
bitcoin_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: cake-update-v3
|
||||
resolved-ref: df9204144011ed9419eff7d9ef3143102a40252d
|
||||
ref: silent-payments
|
||||
resolved-ref: "58d78564cf889af7bfea42eee48ebef467619b97"
|
||||
url: "https://github.com/cake-tech/bitcoin_flutter.git"
|
||||
source: git
|
||||
version: "2.0.2"
|
||||
|
@ -104,10 +112,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.4.1"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -120,10 +128,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
||||
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "4.0.1"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -136,18 +144,18 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||
sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
version: "2.4.7"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
|
||||
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.7"
|
||||
version: "7.2.10"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -160,10 +168,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
|
||||
sha256: "69acb7007eb2a31dc901512bfe0f7b767168be34cb734835d54c070bfa74c1b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.4.3"
|
||||
version: "8.8.0"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -176,10 +184,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.0.3"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -192,10 +200,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
|
||||
sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
version: "4.8.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -216,18 +224,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.3"
|
||||
cryptography:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cryptography
|
||||
sha256: e0e37f79665cd5c86e8897f9abe1accfe813c0cc5299dab22256e22fddc1fef8
|
||||
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.5.0"
|
||||
cw_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -243,14 +251,23 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
elliptic:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "."
|
||||
ref: silent-payments
|
||||
resolved-ref: "09343cf1bc03e74715413972dbec62a2505f5011"
|
||||
url: "https://github.com/cake-tech/dart-elliptic"
|
||||
source: git
|
||||
version: "0.3.10"
|
||||
encrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: encrypt
|
||||
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
|
||||
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
version: "5.0.3"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -263,10 +280,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -292,10 +309,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_mobx
|
||||
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
|
||||
sha256: d1d379561fe84675cc099bc42e99456f73a77fff475d0e2e9bda271751a11a91
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6+5"
|
||||
version: "2.2.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -313,18 +330,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
|
||||
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.3.1"
|
||||
hex:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -401,18 +418,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
version: "4.8.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
|
||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.2.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -449,18 +466,26 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: mobx
|
||||
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
|
||||
sha256: "42ae7277ec5c36fa5ce02aa14551065babce3c38a35947330144ff47bc775c75"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3+1"
|
||||
version: "2.2.1"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181"
|
||||
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.3.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -481,74 +506,74 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
|
||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.12"
|
||||
version: "2.1.1"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
|
||||
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.22"
|
||||
version: "2.2.1"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
|
||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.3.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
|
||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.1.1"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.2.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.1.3"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
||||
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.7"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
|
||||
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.2"
|
||||
version: "3.7.3"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -557,30 +582,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
process:
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
version: "6.1.1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
|
||||
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.4"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
|
||||
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.2.3"
|
||||
rxdart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -593,18 +618,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
|
||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
|
||||
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.0.4"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -694,10 +719,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.3.2"
|
||||
unorm_dart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -718,42 +743,42 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "1.1.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.4.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
||||
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
version: "5.0.9"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
|
||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0+3"
|
||||
version: "1.0.3"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
dart: ">=3.0.6 <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
|
|
|
@ -22,7 +22,7 @@ dependencies:
|
|||
bitcoin_flutter:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_flutter.git
|
||||
ref: cake-update-v3
|
||||
ref: silent-payments
|
||||
bitbox:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitbox-flutter.git
|
||||
|
|
|
@ -86,7 +86,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required String password,
|
||||
}) async {
|
||||
final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password);
|
||||
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password);
|
||||
return BitcoinCashWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
password: password,
|
||||
|
|
|
@ -29,6 +29,12 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
|
|||
networkType: networkType);
|
||||
|
||||
@override
|
||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
||||
String getAddress(
|
||||
{required int index, required bitcoin.HDWallet hd, bitcoin.AddressType? addressType}) =>
|
||||
generateP2PKHAddress(hd: hd, index: index, networkType: networkType);
|
||||
|
||||
@override
|
||||
Future<void> setAddressType(dynamic type) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
|||
|
||||
@override
|
||||
Future<BitcoinCashWallet> create(
|
||||
credentials) async {
|
||||
credentials, {bool? isTestnet}) async {
|
||||
final strength = (credentials.seedPhraseLength == 12)
|
||||
? 128
|
||||
: (credentials.seedPhraseLength == 24)
|
||||
|
|
|
@ -24,7 +24,7 @@ dependencies:
|
|||
bitcoin_flutter:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_flutter.git
|
||||
ref: cake-update-v3
|
||||
ref: silent-payments
|
||||
bitbox:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitbox-flutter.git
|
||||
|
|
|
@ -14,6 +14,16 @@ class SyncingSyncStatus extends SyncStatus {
|
|||
|
||||
@override
|
||||
String toString() => '$blocksLeft';
|
||||
|
||||
factory SyncingSyncStatus.fromHeightValues(int chainTip, int initialSyncHeight, int syncHeight) {
|
||||
final track = chainTip - initialSyncHeight;
|
||||
final diff = track - (chainTip - syncHeight);
|
||||
final ptc = diff <= 0 ? 0.0 : diff / track;
|
||||
final left = chainTip - syncHeight;
|
||||
|
||||
// sum 1 because if at the chain tip, will say "0 blocks left"
|
||||
return SyncingSyncStatus(left + 1, ptc);
|
||||
}
|
||||
}
|
||||
|
||||
class SyncedSyncStatus extends SyncStatus {
|
||||
|
@ -51,4 +61,6 @@ class ConnectedSyncStatus extends SyncStatus {
|
|||
class LostConnectionSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
}
|
||||
@override
|
||||
String toString() => 'Reconnecting';
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@ class Unspent {
|
|||
bool isFrozen;
|
||||
String note;
|
||||
|
||||
bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
|
||||
bool get isP2wpkh =>
|
||||
address.startsWith('bc') || address.startsWith('tb') || address.startsWith('ltc');
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ import 'package:cw_core/wallet_info.dart';
|
|||
|
||||
abstract class WalletAddresses {
|
||||
WalletAddresses(this.walletInfo)
|
||||
: addressesMap = {},
|
||||
addressInfos = {};
|
||||
: addressesMap = {},
|
||||
addressInfos = {};
|
||||
|
||||
final WalletInfo walletInfo;
|
||||
|
||||
|
@ -38,4 +38,7 @@ abstract class WalletAddresses {
|
|||
}
|
||||
|
||||
bool containsAddress(String address) => addressesMap.containsKey(address);
|
||||
|
||||
dynamic addressPageType;
|
||||
Future<void> setAddressType(dynamic type);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
|
|||
RFK extends WalletCredentials> {
|
||||
WalletType getType();
|
||||
|
||||
Future<WalletBase> create(N credentials);
|
||||
Future<WalletBase> create(N credentials, {bool? isTestnet});
|
||||
|
||||
Future<WalletBase> restoreFromSeed(RFS credentials);
|
||||
|
||||
|
|
|
@ -30,4 +30,9 @@ abstract class EthereumWalletAddressesBase extends WalletAddresses with Store {
|
|||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setAddressType(dynamic type) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
|
|||
final Box<WalletInfo> walletInfoSource;
|
||||
|
||||
@override
|
||||
Future<EthereumWallet> create(EthereumNewWalletCredentials credentials) async {
|
||||
Future<EthereumWallet> create(EthereumNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
|
||||
final strength = (credentials.seedPhraseLength == 12)
|
||||
? 128
|
||||
|
|
|
@ -88,4 +88,9 @@ abstract class HavenWalletAddressesBase extends WalletAddressesWithAccount<Accou
|
|||
@override
|
||||
bool containsAddress(String address) =>
|
||||
addressInfos[account?.id ?? 0]?.any((it) => it.address == address) ?? false;
|
||||
|
||||
@override
|
||||
Future<void> setAddressType(dynamic type) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ class HavenWalletService extends WalletService<
|
|||
WalletType getType() => WalletType.haven;
|
||||
|
||||
@override
|
||||
Future<HavenWallet> create(HavenNewWalletCredentials credentials) async {
|
||||
Future<HavenWallet> create(HavenNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
try {
|
||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||
await haven_wallet_manager.createWallet(
|
||||
|
|
|
@ -116,4 +116,9 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
|
|||
@override
|
||||
bool containsAddress(String address) =>
|
||||
addressInfos[account?.id ?? 0]?.any((it) => it.address == address) ?? false;
|
||||
|
||||
@override
|
||||
Future<void> setAddressType(dynamic type) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ class MoneroWalletService extends WalletService<
|
|||
WalletType getType() => WalletType.monero;
|
||||
|
||||
@override
|
||||
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async {
|
||||
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
try {
|
||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||
await monero_wallet_manager.createWallet(
|
||||
|
|
|
@ -47,4 +47,9 @@ abstract class NanoWalletAddressesBase extends WalletAddresses with Store {
|
|||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setAddressType(dynamic type) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
|||
WalletType getType() => WalletType.nano;
|
||||
|
||||
@override
|
||||
Future<WalletBase> create(NanoNewWalletCredentials credentials) async {
|
||||
Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||
// nano standard:
|
||||
DerivationType derivationType = DerivationType.nano;
|
||||
String seedKey = NanoSeeds.generateSeed();
|
||||
|
|
|
@ -142,27 +142,9 @@ Then we need to generate localization files.
|
|||
|
||||
`$ flutter packages pub run tool/generate_localization.dart`
|
||||
|
||||
Lastly, we will generate mobx models for the project.
|
||||
|
||||
Generate mobx models for `cw_core`:
|
||||
|
||||
`cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
|
||||
|
||||
Generate mobx models for `cw_monero`:
|
||||
|
||||
`cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
|
||||
|
||||
Generate mobx models for `cw_bitcoin`:
|
||||
|
||||
`cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
|
||||
|
||||
Generate mobx models for `cw_haven`:
|
||||
|
||||
`cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..`
|
||||
|
||||
Finally build mobx models for the app:
|
||||
|
||||
`$ flutter packages pub run build_runner build --delete-conflicting-outputs`
|
||||
`$ ./model_generator.sh`
|
||||
|
||||
### 9. Build!
|
||||
|
||||
|
|
|
@ -63,9 +63,9 @@ class CWBitcoin extends Bitcoin {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> generateNewAddress(Object wallet) async {
|
||||
Future<void> generateNewAddress(Object wallet, {String? label}) async {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
await bitcoinWallet.walletAddresses.generateNewAddress();
|
||||
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -105,6 +105,21 @@ class CWBitcoin extends Bitcoin {
|
|||
return bitcoinWallet.walletAddresses.address;
|
||||
}
|
||||
|
||||
String getReceiveAddress(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
return bitcoinWallet.walletAddresses.receiveAddress;
|
||||
}
|
||||
|
||||
btc.SilentPaymentAddress? getSilentAddress(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
return bitcoinWallet.walletAddresses.silentAddress;
|
||||
}
|
||||
|
||||
List<BitcoinAddressRecord> getSilentAddresses(Object wallet) {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
return bitcoinWallet.walletAddresses.silentAddresses;
|
||||
}
|
||||
|
||||
@override
|
||||
String formatterBitcoinAmountToString({required int amount})
|
||||
=> bitcoinAmountToString(amount: amount);
|
||||
|
@ -155,4 +170,4 @@ class CWBitcoin extends Bitcoin {
|
|||
@override
|
||||
TransactionPriority getLitecoinTransactionPrioritySlow()
|
||||
=> LitecoinTransactionPriority.slow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,8 @@ class AddressValidator extends TextValidator {
|
|||
AddressValidator({required CryptoCurrency type})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
useAdditionalValidation: type == CryptoCurrency.btc
|
||||
? bitcoin.Address.validateAddress
|
||||
: null,
|
||||
useAdditionalValidation:
|
||||
type == CryptoCurrency.btc ? bitcoin.Address.validateAddress : null,
|
||||
pattern: getPattern(type),
|
||||
length: getLength(type));
|
||||
|
||||
|
@ -25,7 +24,9 @@ class AddressValidator extends TextValidator {
|
|||
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}\$';
|
||||
case CryptoCurrency.btc:
|
||||
return '^3[0-9a-zA-Z]{32}\$|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{59}\$';
|
||||
final silentpayments = '^tsp1[0-9a-zA-Z]{113}\$';
|
||||
return '${bitcoin.P2pkhAddress.REGEX.pattern}|${bitcoin.P2shAddress.REGEX.pattern}|${bitcoin.P2wpkhAddress.REGEX.pattern}|${bitcoin.P2trAddress.REGEX.pattern}|$silentpayments';
|
||||
case CryptoCurrency.nano:
|
||||
case CryptoCurrency.nano:
|
||||
return '[0-9a-zA-Z_]';
|
||||
case CryptoCurrency.banano:
|
||||
|
@ -88,7 +89,7 @@ class AddressValidator extends TextValidator {
|
|||
case CryptoCurrency.dai:
|
||||
case CryptoCurrency.dash:
|
||||
case CryptoCurrency.eos:
|
||||
return '[0-9a-zA-Z]';
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.bch:
|
||||
return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q[0-9a-zA-Z]{42}\$|^bitcoincash:q[0-9a-zA-Z]{41}\$|^bitcoincash:q[0-9a-zA-Z]{42}\$';
|
||||
case CryptoCurrency.bnb:
|
||||
|
@ -284,4 +285,5 @@ class AddressValidator extends TextValidator {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
23
lib/core/secure_storage.dart
Normal file
23
lib/core/secure_storage.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
final SecureStorage secureStorageShared = FakeSecureStorage();
|
||||
|
||||
abstract class SecureStorage {
|
||||
Future<String?> read({required String key});
|
||||
Future<void> write({required String key, required String? value});
|
||||
Future<void> delete({required String key});
|
||||
// Legacy
|
||||
Future<String?> readNoIOptions({required String key});
|
||||
}
|
||||
|
||||
class FakeSecureStorage extends SecureStorage {
|
||||
@override
|
||||
Future<String?> read({required String key}) async => null;
|
||||
|
||||
@override
|
||||
Future<void> write({required String key, required String? value}) async {}
|
||||
|
||||
@override
|
||||
Future<void> delete({required String key}) async {}
|
||||
|
||||
@override
|
||||
Future<String?> readNoIOptions({required String key}) async => null;
|
||||
}
|
|
@ -3,7 +3,9 @@ import 'package:cw_core/sync_status.dart';
|
|||
|
||||
String syncStatusTitle(SyncStatus syncStatus) {
|
||||
if (syncStatus is SyncingSyncStatus) {
|
||||
return S.current.Blocks_remaining('${syncStatus.blocksLeft}');
|
||||
return syncStatus.blocksLeft == 1
|
||||
? S.current.Block_remaining('${syncStatus.blocksLeft}')
|
||||
: S.current.Blocks_remaining('${syncStatus.blocksLeft}');
|
||||
}
|
||||
|
||||
if (syncStatus is SyncedSyncStatus) {
|
||||
|
@ -35,4 +37,4 @@ String syncStatusTitle(SyncStatus syncStatus) {
|
|||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ class WalletCreationService {
|
|||
}
|
||||
}
|
||||
|
||||
Future<WalletBase> create(WalletCredentials credentials) async {
|
||||
Future<WalletBase> create(WalletCredentials credentials, {bool? isTestnet}) async {
|
||||
checkIfExists(credentials.name);
|
||||
final password = generateWalletPassword();
|
||||
credentials.password = password;
|
||||
|
@ -63,7 +63,7 @@ class WalletCreationService {
|
|||
credentials.seedPhraseLength = settingsStore.seedPhraseLength.value;
|
||||
}
|
||||
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
||||
final wallet = await _service!.create(credentials);
|
||||
final wallet = await _service!.create(credentials, isTestnet: isTestnet);
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
await sharedPreferences.setBool(
|
||||
|
|
29
lib/di.dart
29
lib/di.dart
|
@ -222,6 +222,7 @@ import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
|||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc;
|
||||
|
||||
import 'core/totp_request_details.dart';
|
||||
import 'src/screens/settings/desktop_settings/desktop_settings_page.dart';
|
||||
|
@ -341,10 +342,9 @@ Future<void> setup({
|
|||
getIt.get<KeyService>(),
|
||||
(WalletType type) => getIt.get<WalletService>(param1: type)));
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
|
||||
WalletNewVM(getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
type: type));
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) => WalletNewVM(
|
||||
getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
type: type));
|
||||
|
||||
getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
|
||||
return WalletRestorationFromQRVM(getIt.get<AppStore>(),
|
||||
|
@ -524,7 +524,7 @@ Future<void> setup({
|
|||
|
||||
getIt.registerFactory<DesktopSettingsPage>(() => DesktopSettingsPage());
|
||||
|
||||
getIt.registerFactoryParam<ReceiveOptionViewModel, ReceivePageOption?, void>(
|
||||
getIt.registerFactoryParam<ReceiveOptionViewModel, dynamic, void>(
|
||||
(pageOption, _) => ReceiveOptionViewModel(getIt.get<AppStore>().wallet!, pageOption));
|
||||
|
||||
getIt.registerFactoryParam<AnonInvoicePageViewModel, List<dynamic>, void>((args, _) {
|
||||
|
@ -642,7 +642,10 @@ Future<void> setup({
|
|||
|
||||
getIt.registerFactory<MoneroAccountListViewModel>(() {
|
||||
final wallet = getIt.get<AppStore>().wallet!;
|
||||
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
|
||||
if ((wallet.type == WalletType.bitcoin &&
|
||||
wallet.walletAddresses.addressPageType == btc.AddressType.p2sp) ||
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.haven) {
|
||||
return MoneroAccountListViewModel(wallet);
|
||||
}
|
||||
throw Exception(
|
||||
|
@ -900,8 +903,8 @@ Future<void> setup({
|
|||
(param1, isCreate) => NewWalletTypePage(onTypeSelected: param1, isCreate: isCreate ?? true));
|
||||
|
||||
getIt.registerFactoryParam<PreSeedPage, WalletType, AdvancedPrivacySettingsViewModel>(
|
||||
(WalletType type, AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel)
|
||||
=> PreSeedPage(type, advancedPrivacySettingsViewModel));
|
||||
(WalletType type, AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel) =>
|
||||
PreSeedPage(type, advancedPrivacySettingsViewModel));
|
||||
|
||||
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
|
||||
TradeDetailsViewModel(
|
||||
|
@ -995,11 +998,10 @@ Future<void> setup({
|
|||
|
||||
getIt.registerFactory(() => YatService());
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
AddressResolver(
|
||||
yatService: getIt.get<YatService>(),
|
||||
wallet: getIt.get<AppStore>().wallet!,
|
||||
settingsStore: getIt.get<SettingsStore>()));
|
||||
getIt.registerFactory(() => AddressResolver(
|
||||
yatService: getIt.get<YatService>(),
|
||||
wallet: getIt.get<AppStore>().wallet!,
|
||||
settingsStore: getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactoryParam<FullscreenQRPage, QrViewData, void>(
|
||||
(QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
|
||||
|
@ -1175,6 +1177,5 @@ Future<void> setup({
|
|||
getIt.registerFactory(
|
||||
() => WalletConnectConnectionsView(web3walletService: getIt.get<Web3WalletService>()));
|
||||
|
||||
|
||||
_isSetupFinished = true;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import 'package:mobx/mobx.dart';
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
|
||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
|
||||
class AddressPage extends BasePage {
|
||||
AddressPage({
|
||||
|
@ -70,7 +71,7 @@ class AddressPage extends BasePage {
|
|||
size: 16,
|
||||
);
|
||||
final _closeButton =
|
||||
currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
|
||||
currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
|
||||
|
||||
bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
|
||||
|
||||
|
@ -161,10 +162,10 @@ class AddressPage extends BasePage {
|
|||
Observer(builder: (_) {
|
||||
if (addressListViewModel.hasAddressList) {
|
||||
return GestureDetector(
|
||||
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled
|
||||
onTap: () async => !addressListViewModel.hasSilentAddresses &&
|
||||
dashboardViewModel.isAutoGenerateSubaddressesEnabled
|
||||
? await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||
context: context, builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||
: Navigator.of(context).pushNamed(Routes.receive),
|
||||
child: Container(
|
||||
height: 50,
|
||||
|
@ -184,26 +185,30 @@ class AddressPage extends BasePage {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Observer(
|
||||
builder: (_) {
|
||||
String label = addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts_subaddresses
|
||||
: S.of(context).addresses;
|
||||
builder: (_) {
|
||||
String label = addressListViewModel.hasSilentAddresses
|
||||
? S.of(context).labeled_silent_addresses
|
||||
: addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts_subaddresses
|
||||
: S.of(context).addresses;
|
||||
|
||||
if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) {
|
||||
label = addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts
|
||||
: S.of(context).account;
|
||||
}
|
||||
return Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.textColor),
|
||||
);
|
||||
},),
|
||||
if (!addressListViewModel.hasSilentAddresses &&
|
||||
dashboardViewModel.isAutoGenerateSubaddressesEnabled) {
|
||||
label = addressListViewModel.hasAccounts
|
||||
? S.of(context).accounts
|
||||
: S.of(context).account;
|
||||
}
|
||||
return Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.textColor),
|
||||
);
|
||||
},
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
|
@ -213,7 +218,8 @@ class AddressPage extends BasePage {
|
|||
),
|
||||
),
|
||||
);
|
||||
} else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled || addressListViewModel.showElectrumAddressDisclaimer) {
|
||||
} else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled ||
|
||||
addressListViewModel.showElectrumAddressDisclaimer) {
|
||||
return Text(S.of(context).electrum_address_disclaimer,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
|
@ -233,7 +239,7 @@ class AddressPage extends BasePage {
|
|||
return;
|
||||
}
|
||||
|
||||
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
|
||||
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (dynamic option) async {
|
||||
switch (option) {
|
||||
case ReceivePageOption.anonPayInvoice:
|
||||
Navigator.pushNamed(
|
||||
|
@ -265,6 +271,12 @@ class AddressPage extends BasePage {
|
|||
);
|
||||
}
|
||||
break;
|
||||
case bitcoin.AddressType.p2pkh:
|
||||
case bitcoin.AddressType.p2wpkh:
|
||||
case bitcoin.AddressType.p2tr:
|
||||
case bitcoin.AddressType.p2sp:
|
||||
await addressListViewModel.setAddressType(option);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/entities/generate_name.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
|
@ -125,15 +126,20 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
|||
hintStyle: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor),
|
||||
color:
|
||||
Theme.of(context).extension<NewWalletTheme>()!.hintTextColor),
|
||||
hintText: S.of(context).wallet_name,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
|
||||
color: Theme.of(context)
|
||||
.extension<NewWalletTheme>()!
|
||||
.underlineColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
|
||||
color: Theme.of(context)
|
||||
.extension<NewWalletTheme>()!
|
||||
.underlineColor,
|
||||
width: 1.0),
|
||||
),
|
||||
suffixIcon: Semantics(
|
||||
|
@ -160,7 +166,9 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
|||
height: 34,
|
||||
child: Image.asset(
|
||||
'assets/images/refresh_icon.png',
|
||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
|
||||
color: Theme.of(context)
|
||||
.extension<SendPageTheme>()!
|
||||
.textFieldButtonIconColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -168,6 +176,12 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
|||
),
|
||||
validator: WalletNameValidator(),
|
||||
),
|
||||
Observer(builder: (context) {
|
||||
return SettingsSwitcherCell(
|
||||
title: S.current.use_testnet,
|
||||
value: widget._walletNewVM.useTestnet,
|
||||
onValueChange: (_, __) => widget._walletNewVM.toggleUseTestnet());
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -190,7 +190,7 @@ class AnonPayInvoicePage extends BasePage {
|
|||
return;
|
||||
}
|
||||
|
||||
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
|
||||
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (dynamic option) {
|
||||
switch (option) {
|
||||
case ReceivePageOption.mainnet:
|
||||
Navigator.popAndPushNamed(context, Routes.addressPage);
|
||||
|
|
|
@ -24,6 +24,7 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
|
|||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
|
||||
class ReceivePage extends BasePage {
|
||||
ReceivePage({required this.addressListViewModel})
|
||||
|
@ -67,8 +68,7 @@ class ReceivePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget Function(BuildContext, Widget) get rootWrapper =>
|
||||
(BuildContext context, Widget scaffold) =>
|
||||
GradientBackground(scaffold: scaffold);
|
||||
(BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
|
@ -99,7 +99,10 @@ class ReceivePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return (addressListViewModel.type == WalletType.monero ||
|
||||
return ((addressListViewModel.type == WalletType.bitcoin &&
|
||||
addressListViewModel.wallet.walletAddresses.addressPageType ==
|
||||
bitcoin.AddressType.p2sp) ||
|
||||
addressListViewModel.type == WalletType.monero ||
|
||||
addressListViewModel.type == WalletType.haven ||
|
||||
addressListViewModel.type == WalletType.nano ||
|
||||
addressListViewModel.type == WalletType.banano)
|
||||
|
@ -156,7 +159,8 @@ class ReceivePage extends BasePage {
|
|||
icon: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
color:
|
||||
Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -164,11 +168,12 @@ class ReceivePage extends BasePage {
|
|||
cell = HeaderTile(
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.newSubaddress),
|
||||
title: S.of(context).addresses,
|
||||
title: S.of(context).labeled_silent_addresses,
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
color:
|
||||
Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -177,11 +182,19 @@ class ReceivePage extends BasePage {
|
|||
final isCurrent =
|
||||
item.address == addressListViewModel.address.address;
|
||||
final backgroundColor = isCurrent
|
||||
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileBackgroundColor
|
||||
: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor;
|
||||
? Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.currentTileBackgroundColor
|
||||
: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.tilesBackgroundColor;
|
||||
final textColor = isCurrent
|
||||
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileTextColor
|
||||
: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
|
||||
? Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.currentTileTextColor
|
||||
: Theme.of(context)
|
||||
.extension<ReceivePageTheme>()!
|
||||
.tilesTextColor;
|
||||
|
||||
return AddressCell.fromItem(item,
|
||||
isCurrent: isCurrent,
|
||||
|
@ -202,6 +215,15 @@ class ReceivePage extends BasePage {
|
|||
child: cell,
|
||||
);
|
||||
})),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
||||
child: Text(S.of(context).electrum_address_disclaimer,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color:
|
||||
Theme.of(context).extension<BalancePageTheme>()!.labelTextColor)),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
|
|||
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
@ -164,7 +165,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
|||
},
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.qrCode,
|
||||
if (DeviceInfo.instance.isMobile) AddressTextFieldOption.qrCode,
|
||||
AddressTextFieldOption.addressBook
|
||||
],
|
||||
buttonColor:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -130,8 +131,7 @@ class AddressTextField extends StatelessWidget {
|
|||
),
|
||||
)),
|
||||
],
|
||||
if (this.options.contains(AddressTextFieldOption.qrCode) &&
|
||||
DeviceInfo.instance.isMobile) ...[
|
||||
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
|
||||
Container(
|
||||
width: prefixIconWidth,
|
||||
height: prefixIconHeight,
|
||||
|
@ -191,7 +191,7 @@ class AddressTextField extends StatelessWidget {
|
|||
|
||||
Future<void> _presentQRScanner(BuildContext context) async {
|
||||
bool isCameraPermissionGranted =
|
||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||
if (!isCameraPermissionGranted) return;
|
||||
final code = await presentQRScanner();
|
||||
if (code.isEmpty) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import 'package:cw_core/transaction_info.dart';
|
|||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
|
||||
part 'dashboard_view_model.g.dart';
|
||||
|
||||
|
@ -260,7 +261,11 @@ abstract class DashboardViewModelBase with Store {
|
|||
@observable
|
||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> wallet;
|
||||
|
||||
bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
|
||||
bool get hasRescan =>
|
||||
(wallet.type == WalletType.bitcoin &&
|
||||
wallet.walletAddresses.addressPageType == bitcoin.AddressType.p2sp) ||
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.haven;
|
||||
|
||||
BalanceViewModel balanceViewModel;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:cake_wallet/entities/receive_page_option.dart';
|
|||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
|
||||
part 'receive_option_view_model.g.dart';
|
||||
|
||||
|
@ -9,26 +10,38 @@ class ReceiveOptionViewModel = ReceiveOptionViewModelBase with _$ReceiveOptionVi
|
|||
|
||||
abstract class ReceiveOptionViewModelBase with Store {
|
||||
ReceiveOptionViewModelBase(this._wallet, this.initialPageOption)
|
||||
: selectedReceiveOption = initialPageOption ?? ReceivePageOption.mainnet,
|
||||
: selectedReceiveOption = initialPageOption ??
|
||||
(_wallet.type == WalletType.bitcoin
|
||||
? _wallet.walletAddresses.addressPageType
|
||||
: ReceivePageOption.mainnet),
|
||||
_options = [] {
|
||||
final walletType = _wallet.type;
|
||||
_options =
|
||||
walletType == WalletType.haven ? [ReceivePageOption.mainnet] : ReceivePageOption.values;
|
||||
_options = walletType == WalletType.haven
|
||||
? [ReceivePageOption.mainnet]
|
||||
: walletType == WalletType.bitcoin
|
||||
? [
|
||||
bitcoin.AddressType.p2pkh,
|
||||
bitcoin.AddressType.p2wpkh,
|
||||
bitcoin.AddressType.p2tr,
|
||||
bitcoin.AddressType.p2sp,
|
||||
...ReceivePageOption.values.where((element) => element != ReceivePageOption.mainnet)
|
||||
]
|
||||
: ReceivePageOption.values;
|
||||
}
|
||||
|
||||
final WalletBase _wallet;
|
||||
|
||||
final ReceivePageOption? initialPageOption;
|
||||
final dynamic initialPageOption;
|
||||
|
||||
List<ReceivePageOption> _options;
|
||||
List<dynamic> _options;
|
||||
|
||||
@observable
|
||||
ReceivePageOption selectedReceiveOption;
|
||||
dynamic selectedReceiveOption;
|
||||
|
||||
List<ReceivePageOption> get options => _options;
|
||||
List<dynamic> get options => _options;
|
||||
|
||||
@action
|
||||
void selectReceiveOption(ReceivePageOption option) {
|
||||
void selectReceiveOption(dynamic option) {
|
||||
selectedReceiveOption = option;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,10 @@ import 'package:permission_handler/permission_handler.dart';
|
|||
|
||||
part 'node_create_or_edit_view_model.g.dart';
|
||||
|
||||
class NodeCreateOrEditViewModel = NodeCreateOrEditViewModelBase
|
||||
with _$NodeCreateOrEditViewModel;
|
||||
class NodeCreateOrEditViewModel = NodeCreateOrEditViewModelBase with _$NodeCreateOrEditViewModel;
|
||||
|
||||
abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
NodeCreateOrEditViewModelBase(
|
||||
this._nodeSource, this._walletType, this._settingsStore)
|
||||
NodeCreateOrEditViewModelBase(this._nodeSource, this._walletType, this._settingsStore)
|
||||
: state = InitialExecutionState(),
|
||||
connectionState = InitialExecutionState(),
|
||||
useSSL = false,
|
||||
|
@ -65,6 +63,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
|||
bool get hasAuthCredentials =>
|
||||
_walletType == WalletType.monero || _walletType == WalletType.haven;
|
||||
|
||||
bool get hasTestnetSupport => _walletType == WalletType.bitcoin;
|
||||
|
||||
String get uri {
|
||||
var uri = address;
|
||||
|
||||
|
@ -181,7 +181,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
|||
Future<void> scanQRCodeForNewNode(BuildContext context) async {
|
||||
try {
|
||||
bool isCameraPermissionGranted =
|
||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||
if (!isCameraPermissionGranted) return;
|
||||
String code = await presentQRScanner();
|
||||
|
||||
|
@ -196,7 +196,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
|||
}
|
||||
|
||||
final userInfo = uri.userInfo.split(':');
|
||||
|
||||
|
||||
if (userInfo.length < 2) {
|
||||
throw Exception('Unexpected scan QR code value: Value is invalid');
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'rescan_view_model.g.dart';
|
||||
|
@ -9,8 +10,8 @@ enum RescanWalletState { rescaning, none }
|
|||
|
||||
abstract class RescanViewModelBase with Store {
|
||||
RescanViewModelBase(this._wallet)
|
||||
: state = RescanWalletState.none,
|
||||
isButtonEnabled = false;
|
||||
: state = RescanWalletState.none,
|
||||
isButtonEnabled = false;
|
||||
|
||||
final WalletBase _wallet;
|
||||
|
||||
|
@ -23,8 +24,8 @@ abstract class RescanViewModelBase with Store {
|
|||
@action
|
||||
Future<void> rescanCurrentWallet({required int restoreHeight}) async {
|
||||
state = RescanWalletState.rescaning;
|
||||
await _wallet.rescan(height: restoreHeight);
|
||||
_wallet.transactionHistory.clear();
|
||||
_wallet.rescan(height: restoreHeight);
|
||||
if (_wallet.type != WalletType.bitcoin) _wallet.transactionHistory.clear();
|
||||
state = RescanWalletState.none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
@ -27,8 +28,7 @@ class AddressEditOrCreateStateFailure extends AddressEditOrCreateState {
|
|||
}
|
||||
|
||||
abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
||||
WalletAddressEditOrCreateViewModelBase(
|
||||
{required WalletBase wallet, WalletAddressListItem? item})
|
||||
WalletAddressEditOrCreateViewModelBase({required WalletBase wallet, WalletAddressListItem? item})
|
||||
: isEdit = item != null,
|
||||
state = AddressEditOrCreateStateInitial(),
|
||||
label = item?.name ?? '',
|
||||
|
@ -68,27 +68,21 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
|||
if (wallet.type == WalletType.bitcoin
|
||||
|| wallet.type == WalletType.litecoin
|
||||
|| wallet.type == WalletType.bitcoinCash) {
|
||||
await bitcoin!.generateNewAddress(wallet);
|
||||
await bitcoin!.generateNewAddress(wallet, label: label);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.monero) {
|
||||
await monero
|
||||
!.getSubaddressList(wallet)
|
||||
.addSubaddress(
|
||||
wallet,
|
||||
accountIndex: monero!.getCurrentAccount(wallet).id,
|
||||
label: label);
|
||||
await monero!
|
||||
.getSubaddressList(wallet)
|
||||
.addSubaddress(wallet, accountIndex: monero!.getCurrentAccount(wallet).id, label: label);
|
||||
await wallet.save();
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.haven) {
|
||||
await haven
|
||||
!.getSubaddressList(wallet)
|
||||
.addSubaddress(
|
||||
wallet,
|
||||
accountIndex: haven!.getCurrentAccount(wallet).id,
|
||||
label: label);
|
||||
await haven!
|
||||
.getSubaddressList(wallet)
|
||||
.addSubaddress(wallet, accountIndex: haven!.getCurrentAccount(wallet).id, label: label);
|
||||
await wallet.save();
|
||||
}
|
||||
}
|
||||
|
@ -109,9 +103,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
|
|||
}
|
||||
if (wallet.type == WalletType.haven) {
|
||||
await haven!.getSubaddressList(wallet).setLabelSubaddress(wallet,
|
||||
accountIndex: haven!.getCurrentAccount(wallet).id,
|
||||
addressIndex: index,
|
||||
label: label);
|
||||
accountIndex: haven!.getCurrentAccount(wallet).id, addressIndex: index, label: label);
|
||||
await wallet.save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
|||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/haven/haven.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc;
|
||||
|
||||
part 'wallet_address_list_view_model.g.dart';
|
||||
|
||||
|
@ -109,7 +110,7 @@ class EthereumURI extends PaymentURI {
|
|||
|
||||
class BitcoinCashURI extends PaymentURI {
|
||||
BitcoinCashURI({required String amount, required String address})
|
||||
: super(amount: amount, address: address);
|
||||
: super(amount: amount, address: address);
|
||||
@override
|
||||
String toString() {
|
||||
var base = address;
|
||||
|
@ -120,9 +121,7 @@ class BitcoinCashURI extends PaymentURI {
|
|||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class NanoURI extends PaymentURI {
|
||||
NanoURI({required String amount, required String address})
|
||||
|
@ -147,8 +146,6 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
}) : _baseItems = <ListItem>[],
|
||||
selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type),
|
||||
_cryptoNumberFormat = NumberFormat(_cryptoNumberPattern),
|
||||
hasAccounts =
|
||||
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
|
||||
amount = '',
|
||||
super(appStore: appStore) {
|
||||
_init();
|
||||
|
@ -159,7 +156,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
_init();
|
||||
|
||||
selectedCurrency = walletTypeToCryptoCurrency(wallet.type);
|
||||
hasAccounts = wallet.type == WalletType.monero || wallet.type == WalletType.haven;
|
||||
_hasAccounts =
|
||||
hasSilentAddresses || wallet.type == WalletType.monero || wallet.type == WalletType.haven;
|
||||
}
|
||||
|
||||
static const String _cryptoNumberPattern = '0.00000000';
|
||||
|
@ -257,13 +255,19 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
}
|
||||
|
||||
if (wallet.type == WalletType.bitcoin) {
|
||||
final primaryAddress = bitcoin!.getAddress(wallet);
|
||||
final bitcoinAddresses = bitcoin!.getAddresses(wallet).map((addr) {
|
||||
final isPrimary = addr == primaryAddress;
|
||||
final receiveAddress = bitcoin!.getReceiveAddress(wallet);
|
||||
addressList.add(
|
||||
WalletAddressListItem(isPrimary: true, name: 'Primary address', address: receiveAddress));
|
||||
|
||||
return WalletAddressListItem(isPrimary: isPrimary, name: null, address: addr);
|
||||
final silentAddress = bitcoin!.getSilentAddress(wallet).toString();
|
||||
addressList.add(
|
||||
WalletAddressListItem(isPrimary: false, name: silentAddress, address: silentAddress));
|
||||
|
||||
final silentAddresses = bitcoin!.getSilentAddresses(wallet);
|
||||
silentAddresses.forEach((addr) {
|
||||
addressList.add(WalletAddressListItem(
|
||||
isPrimary: false, name: addr.silentAddressLabel, address: addr.address));
|
||||
});
|
||||
addressList.addAll(bitcoinAddresses);
|
||||
}
|
||||
|
||||
if (wallet.type == WalletType.ethereum) {
|
||||
|
@ -276,7 +280,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
}
|
||||
|
||||
@observable
|
||||
bool hasAccounts;
|
||||
bool _hasAccounts = false;
|
||||
|
||||
@computed
|
||||
bool get hasAccounts => _hasAccounts;
|
||||
|
||||
@computed
|
||||
String get accountLabel {
|
||||
|
@ -291,18 +298,31 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
return '';
|
||||
}
|
||||
|
||||
@observable
|
||||
// ignore: prefer_final_fields
|
||||
bool? _hasSilentAddresses = null;
|
||||
|
||||
@computed
|
||||
bool get hasSilentAddresses =>
|
||||
_hasSilentAddresses ??
|
||||
wallet.type == WalletType.bitcoin &&
|
||||
wallet.walletAddresses.addressPageType == btc.AddressType.p2sp;
|
||||
|
||||
@computed
|
||||
bool get hasAddressList =>
|
||||
hasSilentAddresses ||
|
||||
wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.haven;/* ||
|
||||
wallet.type ==
|
||||
WalletType
|
||||
.haven; /* ||
|
||||
wallet.type == WalletType.nano ||
|
||||
wallet.type == WalletType.banano;*/// TODO: nano accounts are disabled for now
|
||||
wallet.type == WalletType.banano;*/ // TODO: nano accounts are disabled for now
|
||||
|
||||
@computed
|
||||
bool get showElectrumAddressDisclaimer =>
|
||||
wallet.type == WalletType.bitcoin ||
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
(wallet.type == WalletType.bitcoin && !hasSilentAddresses) ||
|
||||
wallet.type == WalletType.litecoin ||
|
||||
wallet.type == WalletType.bitcoinCash;
|
||||
|
||||
List<ListItem> _baseItems;
|
||||
|
||||
|
@ -312,13 +332,22 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
void setAddress(WalletAddressListItem address) =>
|
||||
wallet.walletAddresses.address = address.address;
|
||||
|
||||
@action
|
||||
Future<void> setAddressType(dynamic option) async {
|
||||
await wallet.walletAddresses.setAddressType(option);
|
||||
_hasSilentAddresses = option == btc.AddressType.p2sp;
|
||||
}
|
||||
|
||||
void _init() {
|
||||
_baseItems = [];
|
||||
|
||||
if (wallet.type == WalletType.monero ||
|
||||
wallet.type == WalletType.haven /*||
|
||||
wallet.type ==
|
||||
WalletType
|
||||
.haven /*||
|
||||
wallet.type == WalletType.nano ||
|
||||
wallet.type == WalletType.banano*/) {
|
||||
wallet.type == WalletType.banano*/
|
||||
) {
|
||||
_baseItems.add(WalletAccountListHeader());
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ abstract class WalletCreationVMBase with Store {
|
|||
|
||||
bool typeExists(WalletType type) => walletCreationService.typeExists(type);
|
||||
|
||||
Future<void> create({dynamic options, RestoredWallet? restoreWallet}) async {
|
||||
Future<void> create({dynamic options, RestoredWallet? restoreWallet, bool? isTestnet}) async {
|
||||
final type = restoreWallet?.type ?? this.type;
|
||||
try {
|
||||
state = IsExecutingState();
|
||||
|
|
|
@ -27,6 +27,12 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
|||
: selectedMnemonicLanguage = '',
|
||||
super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: false);
|
||||
|
||||
@observable
|
||||
bool _useTestnet = false;
|
||||
|
||||
@computed
|
||||
bool get useTestnet => _useTestnet;
|
||||
|
||||
@observable
|
||||
String selectedMnemonicLanguage;
|
||||
|
||||
|
@ -59,6 +65,9 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
|||
@override
|
||||
Future<WalletBase> process(WalletCredentials credentials) async {
|
||||
walletCreationService.changeWalletType(type: type);
|
||||
return walletCreationService.create(credentials);
|
||||
return walletCreationService.create(credentials, isTestnet: useTestnet);
|
||||
}
|
||||
|
||||
@action
|
||||
void toggleUseTestnet() => _useTestnet = !_useTestnet;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_bitcoin_cash && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||
#!/bin/bash
|
||||
|
||||
cd cw_core; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
|
||||
cd cw_monero; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
|
||||
cd cw_bitcoin; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
|
||||
cd cw_haven; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
|
||||
cd cw_ethereum; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
|
||||
cd cw_nano; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
|
||||
cd cw_bitcoin_cash; flutter pub get; dart run build_runner build --delete-conflicting-outputs; cd ..
|
||||
dart run build_runner build --delete-conflicting-outputs
|
||||
|
|
|
@ -96,7 +96,7 @@ dependencies:
|
|||
bitcoin_flutter:
|
||||
git:
|
||||
url: https://github.com/cake-tech/bitcoin_flutter.git
|
||||
ref: cake-update-v3
|
||||
ref: silent-payments
|
||||
fluttertoast: 8.1.4
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
@ -730,5 +730,8 @@
|
|||
"seed_phrase_length": " ﺭﻭﺬﺒﻟﺍ ﺓﺭﺎﺒﻌﻟﺍ ﻝﻮﻃ",
|
||||
"unavailable_balance": " ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ",
|
||||
"unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ",
|
||||
"unspent_change": "يتغير"
|
||||
}
|
||||
"unspent_change": "يتغير",
|
||||
"Block_remaining": "${status} كتلة متبقية",
|
||||
"labeled_silent_addresses": "العناوين الصامتة المسمى",
|
||||
"use_testnet": "استخدم testnet"
|
||||
}
|
|
@ -726,5 +726,8 @@
|
|||
"seed_phrase_length": "Дължина на началната фраза",
|
||||
"unavailable_balance": "Неналично салдо",
|
||||
"unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.",
|
||||
"unspent_change": "Промяна"
|
||||
}
|
||||
"unspent_change": "Промяна",
|
||||
"Block_remaining": "${status} останал блок",
|
||||
"labeled_silent_addresses": "Етикетирани безшумни адреси",
|
||||
"use_testnet": "Използвайте TestNet"
|
||||
}
|
|
@ -726,5 +726,8 @@
|
|||
"seed_phrase_length": "Délka fráze semene",
|
||||
"unavailable_balance": "Nedostupný zůstatek",
|
||||
"unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.",
|
||||
"unspent_change": "Změna"
|
||||
}
|
||||
"unspent_change": "Změna",
|
||||
"Block_remaining": "${status} Blok zbývající",
|
||||
"labeled_silent_addresses": "Označené tiché adresy",
|
||||
"use_testnet": "Použijte testNet"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "Länge der Seed-Phrase",
|
||||
"unavailable_balance": "Nicht verfügbares Guthaben",
|
||||
"unavailable_balance_description": "Nicht verfügbares Guthaben: Diese Summe umfasst Gelder, die in ausstehenden Transaktionen gesperrt sind, und solche, die Sie in Ihren Münzkontrolleinstellungen aktiv eingefroren haben. Gesperrte Guthaben werden verfügbar, sobald die entsprechenden Transaktionen abgeschlossen sind, während eingefrorene Guthaben für Transaktionen nicht zugänglich bleiben, bis Sie sich dazu entschließen, sie wieder freizugeben.",
|
||||
"unspent_change": "Wechselgeld"
|
||||
}
|
||||
"unspent_change": "Wechselgeld",
|
||||
"Block_remaining": "${status} Block verbleibend",
|
||||
"labeled_silent_addresses": "Bezeichnete stille Adressen",
|
||||
"use_testnet": "TESTNET verwenden"
|
||||
}
|
|
@ -735,5 +735,8 @@
|
|||
"seed_phrase_length": "Seed phrase length",
|
||||
"unavailable_balance": "Unavailable balance",
|
||||
"unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.",
|
||||
"unspent_change": "Change"
|
||||
}
|
||||
"unspent_change": "Change",
|
||||
"Block_remaining": "${status} Block Remaining",
|
||||
"labeled_silent_addresses": "Labeled Silent Addresses",
|
||||
"use_testnet": "Use testnet"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "Longitud de la frase inicial",
|
||||
"unavailable_balance": "Saldo no disponible",
|
||||
"unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.",
|
||||
"unspent_change": "Cambiar"
|
||||
}
|
||||
"unspent_change": "Cambiar",
|
||||
"Block_remaining": "${status} bloque restante",
|
||||
"labeled_silent_addresses": "Direcciones silenciosas etiquetadas",
|
||||
"use_testnet": "Use TestNet"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"unavailable_balance_description": "Solde indisponible : ce total comprend les fonds bloqués dans les transactions en attente et ceux que vous avez activement gelés dans vos paramètres de contrôle des pièces. Les soldes bloqués deviendront disponibles une fois leurs transactions respectives terminées, tandis que les soldes gelés resteront inaccessibles aux transactions jusqu'à ce que vous décidiez de les débloquer.",
|
||||
"camera_permission_is_required": "L'autorisation d'accès à la caméra est requise.\nVeuillez l'activer depuis les paramètres de l'application.",
|
||||
"switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer",
|
||||
"unspent_change": "Changement"
|
||||
}
|
||||
"unspent_change": "Changement",
|
||||
"Block_remaining": "${status} bloc restant",
|
||||
"labeled_silent_addresses": "Adresses silencieuses étiquetées",
|
||||
"use_testnet": "Utiliser TestNet"
|
||||
}
|
|
@ -712,5 +712,8 @@
|
|||
"seed_phrase_length": "Tsawon jimlar iri",
|
||||
"unavailable_balance": "Ma'aunin da ba ya samuwa",
|
||||
"unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.",
|
||||
"unspent_change": "Canza"
|
||||
}
|
||||
"unspent_change": "Canza",
|
||||
"Block_remaining": "${status} toshe ragowar",
|
||||
"labeled_silent_addresses": "Mai labarar adireshin shiru",
|
||||
"use_testnet": "Amfani da gwaji"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "बीज वाक्यांश की लंबाई",
|
||||
"unavailable_balance": "अनुपलब्ध शेष",
|
||||
"unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।",
|
||||
"unspent_change": "परिवर्तन"
|
||||
}
|
||||
"unspent_change": "परिवर्तन",
|
||||
"Block_remaining": "${status} शेष ब्लॉक",
|
||||
"labeled_silent_addresses": "मूक पते लेबल",
|
||||
"use_testnet": "टेस्टनेट का उपयोग करें"
|
||||
}
|
|
@ -732,5 +732,8 @@
|
|||
"seed_phrase_length": "Duljina početne fraze",
|
||||
"unavailable_balance": "Nedostupno stanje",
|
||||
"unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.",
|
||||
"unspent_change": "Promijeniti"
|
||||
}
|
||||
"unspent_change": "Promijeniti",
|
||||
"Block_remaining": "${status} ostao blok",
|
||||
"labeled_silent_addresses": "Označene tihe adrese",
|
||||
"use_testnet": "Koristite TestNet"
|
||||
}
|
|
@ -722,5 +722,8 @@
|
|||
"seed_phrase_length": "Panjang frase benih",
|
||||
"unavailable_balance": "Saldo tidak tersedia",
|
||||
"unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.",
|
||||
"unspent_change": "Mengubah"
|
||||
}
|
||||
"unspent_change": "Mengubah",
|
||||
"Block_remaining": "${status} blok tersisa",
|
||||
"labeled_silent_addresses": "Label alamat diam",
|
||||
"use_testnet": "Gunakan TestNet"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "Lunghezza della frase seed",
|
||||
"unavailable_balance": "Saldo non disponibile",
|
||||
"unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.",
|
||||
"unspent_change": "Modifica"
|
||||
}
|
||||
"unspent_change": "Modifica",
|
||||
"Block_remaining": "${status} blocco rimanente",
|
||||
"labeled_silent_addresses": "Indirizzi silenziosi etichettati",
|
||||
"use_testnet": "Usa TestNet"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "シードフレーズの長さ",
|
||||
"unavailable_balance": "利用できない残高",
|
||||
"unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。",
|
||||
"unspent_change": "変化"
|
||||
}
|
||||
"unspent_change": "変化",
|
||||
"Block_remaining": "${status}ブロックの残り",
|
||||
"labeled_silent_addresses": "サイレントアドレスとラベル付けされています",
|
||||
"use_testnet": "TestNetを使用します"
|
||||
}
|
|
@ -732,5 +732,8 @@
|
|||
"seed_phrase_length": "시드 문구 길이",
|
||||
"unavailable_balance": "사용할 수 없는 잔액",
|
||||
"unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.",
|
||||
"unspent_change": "변화"
|
||||
}
|
||||
"unspent_change": "변화",
|
||||
"Block_remaining": "${status} 나머지 블록",
|
||||
"labeled_silent_addresses": "라벨링 된 무음 주소",
|
||||
"use_testnet": "TestNet을 사용하십시오"
|
||||
}
|
|
@ -732,5 +732,8 @@
|
|||
"seed_phrase_length": "မျိုးစေ့စာပိုဒ်တိုအရှည်",
|
||||
"unavailable_balance": "လက်ကျန်ငွေ မရရှိနိုင်ပါ။",
|
||||
"unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။",
|
||||
"unspent_change": "ပေြာင်းလဲခြင်း"
|
||||
}
|
||||
"unspent_change": "ပေြာင်းလဲခြင်း",
|
||||
"Block_remaining": "ကျန်ရှိသော ${status}",
|
||||
"labeled_silent_addresses": "အသံတိတ်အသံတိတ်လိပ်စာများတံဆိပ်ကပ်",
|
||||
"use_testnet": "testnet ကိုသုံးပါ"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "Lengte van de zaadzin",
|
||||
"unavailable_balance": "Onbeschikbaar saldo",
|
||||
"unavailable_balance_description": "Niet-beschikbaar saldo: Dit totaal omvat het geld dat is vergrendeld in lopende transacties en het geld dat u actief hebt bevroren in uw muntcontrole-instellingen. Vergrendelde saldi komen beschikbaar zodra de betreffende transacties zijn voltooid, terwijl bevroren saldi ontoegankelijk blijven voor transacties totdat u besluit ze weer vrij te geven.",
|
||||
"unspent_change": "Wijziging"
|
||||
}
|
||||
"unspent_change": "Wijziging",
|
||||
"Block_remaining": "${status} blok resterend",
|
||||
"labeled_silent_addresses": "Gelabelde stille adressen",
|
||||
"use_testnet": "Gebruik testnet"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "Długość frazy początkowej",
|
||||
"unavailable_balance": "Niedostępne saldo",
|
||||
"unavailable_balance_description": "Niedostępne saldo: Suma ta obejmuje środki zablokowane w transakcjach oczekujących oraz te, które aktywnie zamroziłeś w ustawieniach kontroli monet. Zablokowane salda staną się dostępne po zakończeniu odpowiednich transakcji, natomiast zamrożone salda pozostaną niedostępne dla transakcji, dopóki nie zdecydujesz się ich odblokować.",
|
||||
"unspent_change": "Zmiana"
|
||||
}
|
||||
"unspent_change": "Zmiana",
|
||||
"Block_remaining": "${status} Block pozostały",
|
||||
"labeled_silent_addresses": "Oznaczone ciche adresy",
|
||||
"use_testnet": "Użyj testne"
|
||||
}
|
|
@ -733,5 +733,8 @@
|
|||
"seed_phrase_length": "Comprimento da frase-semente",
|
||||
"unavailable_balance": "Saldo indisponível",
|
||||
"unavailable_balance_description": "Saldo Indisponível: Este total inclui fundos bloqueados em transações pendentes e aqueles que você congelou ativamente nas configurações de controle de moedas. Os saldos bloqueados ficarão disponíveis assim que suas respectivas transações forem concluídas, enquanto os saldos congelados permanecerão inacessíveis para transações até que você decida descongelá-los.",
|
||||
"unspent_change": "Mudar"
|
||||
}
|
||||
"unspent_change": "Mudar",
|
||||
"Block_remaining": "${status} bloco restante",
|
||||
"labeled_silent_addresses": "Endereços silenciosos rotulados",
|
||||
"use_testnet": "Use testNet"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "Длина исходной фразы",
|
||||
"unavailable_balance": "Недоступный баланс",
|
||||
"unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.",
|
||||
"unspent_change": "Изменять"
|
||||
}
|
||||
"unspent_change": "Изменять",
|
||||
"Block_remaining": "${status} оставшееся блок",
|
||||
"labeled_silent_addresses": "Помеченные тихий адреса",
|
||||
"use_testnet": "Используйте Testnet"
|
||||
}
|
|
@ -732,5 +732,8 @@
|
|||
"seed_phrase_length": "ความยาววลีของเมล็ด",
|
||||
"unavailable_balance": "ยอดคงเหลือไม่พร้อมใช้งาน",
|
||||
"unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง",
|
||||
"unspent_change": "เปลี่ยน"
|
||||
}
|
||||
"unspent_change": "เปลี่ยน",
|
||||
"Block_remaining": "${status} เหลือบล็อกที่เหลืออยู่",
|
||||
"labeled_silent_addresses": "ที่อยู่เงียบที่มีป้ายกำกับ",
|
||||
"use_testnet": "ใช้ testnet"
|
||||
}
|
|
@ -729,5 +729,8 @@
|
|||
"seed_phrase_length": "Haba ng parirala ng binhi",
|
||||
"unavailable_balance": "Hindi available na balanse",
|
||||
"unavailable_balance_description": "Hindi Available na Balanse: Kasama sa kabuuang ito ang mga pondong naka-lock sa mga nakabinbing transaksyon at ang mga aktibong na-freeze mo sa iyong mga setting ng kontrol ng coin. Magiging available ang mga naka-lock na balanse kapag nakumpleto na ang kani-kanilang mga transaksyon, habang ang mga nakapirming balanse ay nananatiling hindi naa-access para sa mga transaksyon hanggang sa magpasya kang i-unfreeze ang mga ito.",
|
||||
"unspent_change": "Baguhin"
|
||||
}
|
||||
"unspent_change": "Baguhin",
|
||||
"Block_remaining": "${status} I -block ang natitira",
|
||||
"labeled_silent_addresses": "May label na tahimik na mga address",
|
||||
"use_testnet": "Gumamit ng testnet"
|
||||
}
|
|
@ -732,5 +732,8 @@
|
|||
"seed_phrase_length": "Çekirdek cümle uzunluğu",
|
||||
"unavailable_balance": "Kullanılamayan bakiye",
|
||||
"unavailable_balance_description": "Kullanılamayan Bakiye: Bu toplam, bekleyen işlemlerde kilitlenen fonları ve jeton kontrol ayarlarınızda aktif olarak dondurduğunuz fonları içerir. Kilitli bakiyeler, ilgili işlemleri tamamlandıktan sonra kullanılabilir hale gelir; dondurulmuş bakiyeler ise siz onları dondurmaya karar verene kadar işlemler için erişilemez durumda kalır.",
|
||||
"unspent_change": "Değiştirmek"
|
||||
}
|
||||
"unspent_change": "Değiştirmek",
|
||||
"Block_remaining": "${status} blok kalan blok",
|
||||
"labeled_silent_addresses": "Etiketli sessiz adresler",
|
||||
"use_testnet": "TestNet kullanın"
|
||||
}
|
|
@ -734,5 +734,8 @@
|
|||
"seed_phrase_length": "Довжина початкової фрази",
|
||||
"unavailable_balance": "Недоступний баланс",
|
||||
"unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.",
|
||||
"unspent_change": "Зміна"
|
||||
}
|
||||
"unspent_change": "Зміна",
|
||||
"Block_remaining": "${status} блок залишився",
|
||||
"labeled_silent_addresses": "Позначені мовчазними адресами",
|
||||
"use_testnet": "Використовуйте тестову мережу"
|
||||
}
|
|
@ -726,5 +726,8 @@
|
|||
"seed_phrase_length": " ﯽﺋﺎﺒﻤﻟ ﯽﮐ ﮯﻠﻤﺟ ﮯﮐ ﺞﯿﺑ",
|
||||
"unavailable_balance": " ﺲﻨﻠﯿﺑ ﺏﺎﯿﺘﺳﺩ ﺮﯿﻏ",
|
||||
"unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ",
|
||||
"unspent_change": "تبدیل کریں"
|
||||
"unspent_change": "تبدیل کریں",
|
||||
"Block_remaining": "${status} باقی بلاک",
|
||||
"labeled_silent_addresses": "خاموش پتے لیبل لگا",
|
||||
"use_testnet": "ٹیسٹ نیٹ استعمال کریں"
|
||||
}
|
||||
|
|
|
@ -728,5 +728,8 @@
|
|||
"seed_phrase_length": "Gigun gbolohun irugbin",
|
||||
"unavailable_balance": "Iwontunwonsi ti ko si",
|
||||
"unavailable_balance_description": "Iwontunws.funfun ti ko si: Lapapọ yii pẹlu awọn owo ti o wa ni titiipa ni awọn iṣowo isunmọ ati awọn ti o ti didi ni itara ninu awọn eto iṣakoso owo rẹ. Awọn iwọntunwọnsi titiipa yoo wa ni kete ti awọn iṣowo oniwun wọn ba ti pari, lakoko ti awọn iwọntunwọnsi tio tutunini ko ni iraye si fun awọn iṣowo titi iwọ o fi pinnu lati mu wọn kuro.",
|
||||
"unspent_change": "Yipada"
|
||||
}
|
||||
"unspent_change": "Yipada",
|
||||
"Block_remaining": "${status} Bdund díẹ",
|
||||
"labeled_silent_addresses": "Awọn adirẹsi ipalọlọ",
|
||||
"use_testnet": "Lo tele"
|
||||
}
|
|
@ -733,5 +733,8 @@
|
|||
"seed_phrase_length": "种子短语长度",
|
||||
"unavailable_balance": "不可用余额",
|
||||
"unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。",
|
||||
"unspent_change": "改变"
|
||||
}
|
||||
"unspent_change": "改变",
|
||||
"Block_remaining": "${status}块剩余",
|
||||
"labeled_silent_addresses": "标记为无声地址",
|
||||
"use_testnet": "使用TestNet"
|
||||
}
|
16
scripts/android/shell.nix
Normal file
16
scripts/android/shell.nix
Normal file
|
@ -0,0 +1,16 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgs.curl
|
||||
pkgs.unzip
|
||||
pkgs.automake
|
||||
pkgs.file
|
||||
pkgs.pkg-config
|
||||
pkgs.git
|
||||
pkgs.libtool
|
||||
pkgs.ncurses5
|
||||
pkgs.openjdk8
|
||||
pkgs.clang
|
||||
];
|
||||
}
|
|
@ -71,6 +71,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
|||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as btc;
|
||||
""";
|
||||
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
||||
const bitcoinContent = """
|
||||
|
@ -87,12 +88,15 @@ abstract class Bitcoin {
|
|||
TransactionPriority deserializeBitcoinTransactionPriority(int raw);
|
||||
TransactionPriority deserializeLitecoinTransactionPriority(int raw);
|
||||
int getFeeRate(Object wallet, TransactionPriority priority);
|
||||
Future<void> generateNewAddress(Object wallet);
|
||||
Future<void> generateNewAddress(Object wallet, {String? label});
|
||||
Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate});
|
||||
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate});
|
||||
|
||||
List<String> getAddresses(Object wallet);
|
||||
String getAddress(Object wallet);
|
||||
String getReceiveAddress(Object wallet);
|
||||
btc.SilentPaymentAddress? getSilentAddress(Object wallet);
|
||||
List<BitcoinAddressRecord> getSilentAddresses(Object wallet);
|
||||
|
||||
String formatterBitcoinAmountToString({required int amount});
|
||||
double formatterBitcoinAmountToDouble({required int amount});
|
||||
|
|
Loading…
Reference in a new issue