mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
Btc address types (#1263)
* inital migration changes * feat: rest of changes * minor fix [skip ci] * fix: P2wshAddress & wallet address index * fix: address review comments * fix: address type restore * feat: add testnet * Fix review comments Remove bitcoin_base from cw_core * Fix address not matching selected type on start * remove un-necessary parameter [skip ci] * Remove bitcoin specific code from main lib Fix possible runtime exception from list wrong access * Minor fix * fix: fixes for Testnet * fix: bitcoin receive option dependency breaks monerocom * Fix issues when building Monero.com * feat: Transaction Builder changes * fix: discover addresses, testnet restoring, duplicate unspent coins, and taproot address vs schnorr sig tweak * fix: remove print * feat: improve error when failed broadcast response * feat: create fish shell env script * fix: unmodifiable maps * fix: build * fix: build * fix: computed observable side effect bug * feat: add nix script for android build_all * fix: wrong keypairs used for signing * fix: wrong addresses when using fromScriptPubKey scripts * fix(actual commit): testnet tx expanded + wrong addresses when using fromScriptPubKey scripts (update bitcoin_base deps) * fix: self-send [skip ci] * fix: p2wsh * fix: testnet fees * New versions * Update macos build number Minor UI fix * fix: use new bitcoin_base ref, fix tx list wrong hex value & refactor hidden vs hd use - if always use sideHd for isHidden, it is easier to simplify the functions instead of passing both which can be error prone - (ps: now this could probably be changed, for example from isHidden to isChange since with address list we now see "hidden" addresses) * Fix if condition to handle litecoin case * fix: self-send, change address was always making direction incoming * refactor: improve estimation function, add more inputs if balance missing * fix: new bitcoin_base update, fixes script issues * Update evm chain wallet service arguments * Fix translation [skip ci] * Fix translation [skip ci] * Update strings_fr.arb [skip ci] * fix: async isChange function not being awaited, refactor to reduce looping into a single place * fix: _address vs address, missing p2sh * fix: minor mistake in storing p2sh page type [skip ci] * refactor: use already matched addresses property * feat: improved perfomance for fetching transaction histories * feat: continue perfomance change, improve address discovery only to last address by type with history * fix: make sure transaction list is sorted by date * refactor: isTestnet only for bitcoin * fix: walletInfo type null case * fix: deprecated p2pk * refactor: make condition more readable * refactor: remove unnecessary Str variant * refactor: make condition more readable * fix: infinite loop possible * Revert removing isTestnet from other wallets [skip ci] * refactor: rename addresses when matched by receive type * Make the beta build [skip ci] Remove app_env.fish --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
109bba4301
commit
a3a35f05e1
91 changed files with 1851 additions and 1333 deletions
|
@ -1,5 +1,3 @@
|
||||||
Bitcoin transactions fixes and enhancements
|
Support ALL Bitcoin address types (Legacy, Segwit (both variants), Taproot)
|
||||||
EVM wallets enhancements (Ethereum and Polygon)
|
Enhance Sending/Receiving flow for Bitcoin
|
||||||
Improve wallet recovery and error tolerance
|
Improve fee calculations in Bitcoin
|
||||||
Enhance Background sync for Monero wallets
|
|
||||||
Bug fixes
|
|
|
@ -1,23 +1,23 @@
|
||||||
import 'dart:typed_data';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
|
||||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
|
||||||
|
|
||||||
String addressFromOutput(Uint8List script, bitcoin.NetworkType networkType) {
|
String addressFromOutputScript(Script script, BasedUtxoNetwork network) {
|
||||||
try {
|
try {
|
||||||
return bitcoin.P2PKH(
|
switch (script.getAddressType()) {
|
||||||
data: PaymentData(output: script),
|
case P2pkhAddressType.p2pkh:
|
||||||
network: networkType)
|
return P2pkhAddress.fromScriptPubkey(script: script).toAddress(network);
|
||||||
.data
|
case P2shAddressType.p2pkInP2sh:
|
||||||
.address!;
|
return P2shAddress.fromScriptPubkey(script: script).toAddress(network);
|
||||||
|
case SegwitAddresType.p2wpkh:
|
||||||
|
return P2wpkhAddress.fromScriptPubkey(script: script).toAddress(network);
|
||||||
|
case P2shAddressType.p2pkhInP2sh:
|
||||||
|
return P2shAddress.fromScriptPubkey(script: script).toAddress(network);
|
||||||
|
case SegwitAddresType.p2wsh:
|
||||||
|
return P2wshAddress.fromScriptPubkey(script: script).toAddress(network);
|
||||||
|
case SegwitAddresType.p2tr:
|
||||||
|
return P2trAddress.fromScriptPubkey(script: script).toAddress(network);
|
||||||
|
default:
|
||||||
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
try {
|
|
||||||
return bitcoin.P2WPKH(
|
|
||||||
data: PaymentData(output: script),
|
|
||||||
network: networkType)
|
|
||||||
.data
|
|
||||||
.address!;
|
|
||||||
} catch(_) {}
|
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,9 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin;
|
||||||
import 'package:bs58check/bs58check.dart' as bs58check;
|
|
||||||
import 'package:bitcoin_flutter/src/utils/constants/op.dart';
|
|
||||||
import 'package:bitcoin_flutter/src/utils/script.dart' as bscript;
|
|
||||||
import 'package:bitcoin_flutter/src/address.dart';
|
|
||||||
|
|
||||||
Uint8List p2shAddressToOutputScript(String address) {
|
List<int> addressToOutputScript(String address, bitcoin.BasedUtxoNetwork network) {
|
||||||
final decodeBase58 = bs58check.decode(address);
|
|
||||||
final hash = decodeBase58.sublist(1);
|
|
||||||
return bscript.compile(<dynamic>[OPS['OP_HASH160'], hash, OPS['OP_EQUAL']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8List addressToOutputScript(
|
|
||||||
String address, bitcoin.NetworkType networkType) {
|
|
||||||
try {
|
try {
|
||||||
// FIXME: improve validation for p2sh addresses
|
return bitcoin.addressToOutputScript(address: address, network: network);
|
||||||
// 3 for bitcoin
|
|
||||||
// m for litecoin
|
|
||||||
if (address.startsWith('3') || address.toLowerCase().startsWith('m')) {
|
|
||||||
return p2shAddressToOutputScript(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Address.addressToOutputScript(address, networkType);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print(err);
|
print(err);
|
||||||
return Uint8List(0);
|
return Uint8List(0);
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||||
|
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
|
import 'package:cw_bitcoin/script_hash.dart' as sh;
|
||||||
|
|
||||||
class BitcoinAddressRecord {
|
class BitcoinAddressRecord {
|
||||||
BitcoinAddressRecord(
|
BitcoinAddressRecord(
|
||||||
this.address, {
|
this.address, {
|
||||||
|
@ -10,23 +13,41 @@ class BitcoinAddressRecord {
|
||||||
int balance = 0,
|
int balance = 0,
|
||||||
String name = '',
|
String name = '',
|
||||||
bool isUsed = false,
|
bool isUsed = false,
|
||||||
|
required this.type,
|
||||||
|
String? scriptHash,
|
||||||
|
required this.network,
|
||||||
}) : _txCount = txCount,
|
}) : _txCount = txCount,
|
||||||
_balance = balance,
|
_balance = balance,
|
||||||
_name = name,
|
_name = name,
|
||||||
_isUsed = isUsed;
|
_isUsed = isUsed,
|
||||||
|
scriptHash =
|
||||||
|
scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null);
|
||||||
|
|
||||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork? network) {
|
||||||
final decoded = json.decode(jsonSource) as Map;
|
final decoded = json.decode(jsonSource) as Map;
|
||||||
|
|
||||||
return BitcoinAddressRecord(decoded['address'] as String,
|
return BitcoinAddressRecord(
|
||||||
index: decoded['index'] as int,
|
decoded['address'] as String,
|
||||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
index: decoded['index'] as int,
|
||||||
isUsed: decoded['isUsed'] as bool? ?? false,
|
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||||
txCount: decoded['txCount'] as int? ?? 0,
|
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||||
name: decoded['name'] as String? ?? '',
|
txCount: decoded['txCount'] as int? ?? 0,
|
||||||
balance: decoded['balance'] as int? ?? 0);
|
name: decoded['name'] as String? ?? '',
|
||||||
|
balance: decoded['balance'] as int? ?? 0,
|
||||||
|
type: decoded['type'] != null && decoded['type'] != ''
|
||||||
|
? BitcoinAddressType.values
|
||||||
|
.firstWhere((type) => type.toString() == decoded['type'] as String)
|
||||||
|
: SegwitAddresType.p2wpkh,
|
||||||
|
scriptHash: decoded['scriptHash'] as String?,
|
||||||
|
network: (decoded['network'] as String?) == null
|
||||||
|
? network
|
||||||
|
: BasedUtxoNetwork.fromName(decoded['network'] as String),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
bool isHidden;
|
bool isHidden;
|
||||||
final int index;
|
final int index;
|
||||||
|
@ -34,6 +55,8 @@ class BitcoinAddressRecord {
|
||||||
int _balance;
|
int _balance;
|
||||||
String _name;
|
String _name;
|
||||||
bool _isUsed;
|
bool _isUsed;
|
||||||
|
String? scriptHash;
|
||||||
|
BasedUtxoNetwork? network;
|
||||||
|
|
||||||
int get txCount => _txCount;
|
int get txCount => _txCount;
|
||||||
|
|
||||||
|
@ -50,21 +73,28 @@ class BitcoinAddressRecord {
|
||||||
void setAsUsed() => _isUsed = true;
|
void setAsUsed() => _isUsed = true;
|
||||||
void setNewName(String label) => _name = label;
|
void setNewName(String label) => _name = label;
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => address.hashCode;
|
int get hashCode => address.hashCode;
|
||||||
|
|
||||||
String get cashAddr => bitbox.Address.toCashAddress(address);
|
String get cashAddr => bitbox.Address.toCashAddress(address);
|
||||||
|
|
||||||
|
BitcoinAddressType type;
|
||||||
|
|
||||||
|
String updateScriptHash(BasedUtxoNetwork network) {
|
||||||
|
scriptHash = sh.scriptHash(address, network: network);
|
||||||
|
return scriptHash!;
|
||||||
|
}
|
||||||
|
|
||||||
String toJSON() => json.encode({
|
String toJSON() => json.encode({
|
||||||
'address': address,
|
'address': address,
|
||||||
'index': index,
|
'index': index,
|
||||||
'isHidden': isHidden,
|
'isHidden': isHidden,
|
||||||
|
'isUsed': isUsed,
|
||||||
'txCount': txCount,
|
'txCount': txCount,
|
||||||
'name': name,
|
'name': name,
|
||||||
'isUsed': isUsed,
|
|
||||||
'balance': balance,
|
'balance': balance,
|
||||||
|
'type': type.toString(),
|
||||||
|
'scriptHash': scriptHash,
|
||||||
|
'network': network?.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
42
cw_bitcoin/lib/bitcoin_receive_page_option.dart
Normal file
42
cw_bitcoin/lib/bitcoin_receive_page_option.dart
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
|
|
||||||
|
class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
|
static const p2wpkh = BitcoinReceivePageOption._('Segwit (P2WPKH)');
|
||||||
|
static const p2sh = BitcoinReceivePageOption._('Segwit-Compatible (P2SH)');
|
||||||
|
static const p2tr = BitcoinReceivePageOption._('Taproot (P2TR)');
|
||||||
|
static const p2wsh = BitcoinReceivePageOption._('Segwit (P2WSH)');
|
||||||
|
static const p2pkh = BitcoinReceivePageOption._('Legacy (P2PKH)');
|
||||||
|
|
||||||
|
const BitcoinReceivePageOption._(this.value);
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
|
||||||
|
String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const all = [
|
||||||
|
BitcoinReceivePageOption.p2wpkh,
|
||||||
|
BitcoinReceivePageOption.p2sh,
|
||||||
|
BitcoinReceivePageOption.p2tr,
|
||||||
|
BitcoinReceivePageOption.p2wsh,
|
||||||
|
BitcoinReceivePageOption.p2pkh
|
||||||
|
];
|
||||||
|
|
||||||
|
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
|
||||||
|
switch (type) {
|
||||||
|
case SegwitAddresType.p2tr:
|
||||||
|
return BitcoinReceivePageOption.p2tr;
|
||||||
|
case SegwitAddresType.p2wsh:
|
||||||
|
return BitcoinReceivePageOption.p2wsh;
|
||||||
|
case P2pkhAddressType.p2pkh:
|
||||||
|
return BitcoinReceivePageOption.p2pkh;
|
||||||
|
case P2shAddressType.p2wpkhInP2sh:
|
||||||
|
return BitcoinReceivePageOption.p2sh;
|
||||||
|
case SegwitAddresType.p2wpkh:
|
||||||
|
default:
|
||||||
|
return BitcoinReceivePageOption.p2wpkh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,10 +6,9 @@ class BitcoinUnspent extends Unspent {
|
||||||
: bitcoinAddressRecord = addressRecord,
|
: bitcoinAddressRecord = addressRecord,
|
||||||
super(addressRecord.address, hash, value, vout, null);
|
super(addressRecord.address, hash, value, vout, null);
|
||||||
|
|
||||||
factory BitcoinUnspent.fromJSON(
|
factory BitcoinUnspent.fromJSON(BitcoinAddressRecord address, Map<String, dynamic> json) =>
|
||||||
BitcoinAddressRecord address, Map<String, dynamic> json) =>
|
BitcoinUnspent(
|
||||||
BitcoinUnspent(address, json['tx_hash'] as String, json['value'] as int,
|
address, json['tx_hash'] as String, json['value'] as int, json['tx_pos'] as int);
|
||||||
json['tx_pos'] as int);
|
|
||||||
|
|
||||||
final BitcoinAddressRecord bitcoinAddressRecord;
|
final BitcoinAddressRecord bitcoinAddressRecord;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
@ -17,36 +18,42 @@ part 'bitcoin_wallet.g.dart';
|
||||||
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
||||||
|
|
||||||
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
BitcoinWalletBase(
|
BitcoinWalletBase({
|
||||||
{required String mnemonic,
|
required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required Uint8List seedBytes,
|
required Uint8List seedBytes,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
String? addressPageType,
|
||||||
ElectrumBalance? initialBalance,
|
BasedUtxoNetwork? networkParam,
|
||||||
int initialRegularAddressIndex = 0,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialChangeAddressIndex = 0})
|
ElectrumBalance? initialBalance,
|
||||||
: super(
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
|
}) : super(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
networkType: bitcoin.bitcoin,
|
networkType: networkParam == null
|
||||||
|
? bitcoin.bitcoin
|
||||||
|
: networkParam == BitcoinNetwork.mainnet
|
||||||
|
? bitcoin.bitcoin
|
||||||
|
: bitcoin.testnet,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
currency: CryptoCurrency.btc) {
|
currency: CryptoCurrency.btc) {
|
||||||
walletAddresses = BitcoinWalletAddresses(
|
walletAddresses = BitcoinWalletAddresses(
|
||||||
walletInfo,
|
walletInfo,
|
||||||
electrumClient: electrumClient,
|
electrumClient: electrumClient,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
||||||
.derivePath("m/0'/1"),
|
network: networkParam ?? network,
|
||||||
networkType: networkType);
|
);
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
});
|
});
|
||||||
|
@ -57,21 +64,26 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
|
String? addressPageType,
|
||||||
|
BasedUtxoNetwork? network,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
ElectrumBalance? initialBalance,
|
ElectrumBalance? initialBalance,
|
||||||
int initialRegularAddressIndex = 0,
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
int initialChangeAddressIndex = 0
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
}) async {
|
}) async {
|
||||||
return BitcoinWallet(
|
return BitcoinWallet(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex);
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
|
addressPageType: addressPageType,
|
||||||
|
networkParam: network,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<BitcoinWallet> open({
|
static Future<BitcoinWallet> open({
|
||||||
|
@ -80,16 +92,21 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password);
|
final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password,
|
||||||
|
walletInfo.network != null ? BasedUtxoNetwork.fromName(walletInfo.network!) : null);
|
||||||
|
|
||||||
return BitcoinWallet(
|
return BitcoinWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: snp.addresses,
|
initialAddresses: snp.addresses,
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex);
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
|
addressPageType: snp.addressPageType,
|
||||||
|
networkParam: snp.network,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||||
import 'package:cw_bitcoin/utils.dart';
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
@ -11,24 +10,31 @@ part 'bitcoin_wallet_addresses.g.dart';
|
||||||
class BitcoinWalletAddresses = BitcoinWalletAddressesBase with _$BitcoinWalletAddresses;
|
class BitcoinWalletAddresses = BitcoinWalletAddressesBase with _$BitcoinWalletAddresses;
|
||||||
|
|
||||||
abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
||||||
BitcoinWalletAddressesBase(WalletInfo walletInfo,
|
BitcoinWalletAddressesBase(
|
||||||
{required bitcoin.HDWallet mainHd,
|
WalletInfo walletInfo, {
|
||||||
required bitcoin.HDWallet sideHd,
|
required super.mainHd,
|
||||||
required bitcoin.NetworkType networkType,
|
required super.sideHd,
|
||||||
required ElectrumClient electrumClient,
|
required super.network,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
required super.electrumClient,
|
||||||
int initialRegularAddressIndex = 0,
|
super.initialAddresses,
|
||||||
int initialChangeAddressIndex = 0})
|
super.initialRegularAddressIndex,
|
||||||
: super(walletInfo,
|
super.initialChangeAddressIndex,
|
||||||
initialAddresses: initialAddresses,
|
}) : super(walletInfo);
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
|
||||||
mainHd: mainHd,
|
|
||||||
sideHd: sideHd,
|
|
||||||
electrumClient: electrumClient,
|
|
||||||
networkType: networkType);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
String getAddress({required int index, required HDWallet hd, BitcoinAddressType? addressType}) {
|
||||||
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
if (addressType == P2pkhAddressType.p2pkh)
|
||||||
|
return generateP2PKHAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
|
if (addressType == SegwitAddresType.p2tr)
|
||||||
|
return generateP2TRAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
|
if (addressType == SegwitAddresType.p2wsh)
|
||||||
|
return generateP2WSHAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
|
if (addressType == P2shAddressType.p2wpkhInP2sh)
|
||||||
|
return generateP2SHAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
|
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart';
|
||||||
|
@ -23,12 +24,17 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
WalletType getType() => WalletType.bitcoin;
|
WalletType getType() => WalletType.bitcoin;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
|
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||||
|
credentials.walletInfo?.network = network.value;
|
||||||
|
|
||||||
final wallet = await BitcoinWalletBase.create(
|
final wallet = await BitcoinWalletBase.create(
|
||||||
mnemonic: await generateMnemonic(),
|
mnemonic: await generateMnemonic(),
|
||||||
password: credentials.password!,
|
password: credentials.password!,
|
||||||
walletInfo: credentials.walletInfo!,
|
walletInfo: credentials.walletInfo!,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
|
network: network,
|
||||||
|
);
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -92,20 +98,27 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
|
||||||
|
{bool? isTestnet}) async =>
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
if (!validateMnemonic(credentials.mnemonic)) {
|
if (!validateMnemonic(credentials.mnemonic)) {
|
||||||
throw BitcoinMnemonicIsIncorrectException();
|
throw BitcoinMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||||
|
credentials.walletInfo?.network = network.value;
|
||||||
|
|
||||||
final wallet = await BitcoinWalletBase.create(
|
final wallet = await BitcoinWalletBase.create(
|
||||||
password: credentials.password!,
|
password: credentials.password!,
|
||||||
mnemonic: credentials.mnemonic,
|
mnemonic: credentials.mnemonic,
|
||||||
walletInfo: credentials.walletInfo!,
|
walletInfo: credentials.walletInfo!,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
|
network: network,
|
||||||
|
);
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
|
|
|
@ -2,12 +2,12 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_bitcoin/script_hash.dart';
|
import 'package:cw_bitcoin/script_hash.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
String jsonrpcparams(List<Object> params) {
|
String jsonrpcparams(List<Object> params) {
|
||||||
final _params = params?.map((val) => '"${val.toString()}"')?.join(',');
|
final _params = params?.map((val) => '"${val.toString()}"')?.join(',');
|
||||||
|
@ -22,10 +22,7 @@ String jsonrpc(
|
||||||
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
|
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
|
||||||
|
|
||||||
class SocketTask {
|
class SocketTask {
|
||||||
SocketTask({
|
SocketTask({required this.isSubscription, this.completer, this.subject});
|
||||||
required this.isSubscription,
|
|
||||||
this.completer,
|
|
||||||
this.subject});
|
|
||||||
|
|
||||||
final Completer<dynamic>? completer;
|
final Completer<dynamic>? completer;
|
||||||
final BehaviorSubject<dynamic>? subject;
|
final BehaviorSubject<dynamic>? subject;
|
||||||
|
@ -51,8 +48,7 @@ class ElectrumClient {
|
||||||
Timer? _aliveTimer;
|
Timer? _aliveTimer;
|
||||||
String unterminatedString;
|
String unterminatedString;
|
||||||
|
|
||||||
Future<void> connectToUri(Uri uri) async =>
|
Future<void> connectToUri(Uri uri) async => await connect(host: uri.host, port: uri.port);
|
||||||
await connect(host: uri.host, port: uri.port);
|
|
||||||
|
|
||||||
Future<void> connect({required String host, required int port}) async {
|
Future<void> connect({required String host, required int port}) async {
|
||||||
try {
|
try {
|
||||||
|
@ -104,21 +100,20 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJSONStringCorrect(unterminatedString)) {
|
if (isJSONStringCorrect(unterminatedString)) {
|
||||||
final response =
|
final response = json.decode(unterminatedString) as Map<String, dynamic>;
|
||||||
json.decode(unterminatedString) as Map<String, dynamic>;
|
|
||||||
_handleResponse(response);
|
_handleResponse(response);
|
||||||
unterminatedString = '';
|
unterminatedString = '';
|
||||||
}
|
}
|
||||||
} on TypeError catch (e) {
|
} on TypeError catch (e) {
|
||||||
if (!e.toString().contains('Map<String, Object>') && !e.toString().contains('Map<String, dynamic>')) {
|
if (!e.toString().contains('Map<String, Object>') &&
|
||||||
|
!e.toString().contains('Map<String, dynamic>')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unterminatedString += message;
|
unterminatedString += message;
|
||||||
|
|
||||||
if (isJSONStringCorrect(unterminatedString)) {
|
if (isJSONStringCorrect(unterminatedString)) {
|
||||||
final response =
|
final response = json.decode(unterminatedString) as Map<String, dynamic>;
|
||||||
json.decode(unterminatedString) as Map<String, dynamic>;
|
|
||||||
_handleResponse(response);
|
_handleResponse(response);
|
||||||
// unterminatedString = null;
|
// unterminatedString = null;
|
||||||
unterminatedString = '';
|
unterminatedString = '';
|
||||||
|
@ -142,8 +137,7 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> version() =>
|
Future<List<String>> version() => call(method: 'server.version').then((dynamic result) {
|
||||||
call(method: 'server.version').then((dynamic result) {
|
|
||||||
if (result is List) {
|
if (result is List) {
|
||||||
return result.map((dynamic val) => val.toString()).toList();
|
return result.map((dynamic val) => val.toString()).toList();
|
||||||
}
|
}
|
||||||
|
@ -178,11 +172,10 @@ class ElectrumClient {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
||||||
String address, NetworkType networkType) =>
|
String address, BasedUtxoNetwork network) =>
|
||||||
call(
|
call(
|
||||||
method: 'blockchain.scripthash.listunspent',
|
method: 'blockchain.scripthash.listunspent',
|
||||||
params: [scriptHash(address, networkType: networkType)])
|
params: [scriptHash(address, network: network)]).then((dynamic result) {
|
||||||
.then((dynamic result) {
|
|
||||||
if (result is List) {
|
if (result is List) {
|
||||||
return result.map((dynamic val) {
|
return result.map((dynamic val) {
|
||||||
if (val is Map<String, dynamic>) {
|
if (val is Map<String, dynamic>) {
|
||||||
|
@ -229,8 +222,7 @@ class ElectrumClient {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getTransactionRaw(
|
Future<Map<String, dynamic>> getTransactionRaw({required String hash}) async =>
|
||||||
{required String hash}) async =>
|
|
||||||
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000)
|
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000)
|
||||||
.then((dynamic result) {
|
.then((dynamic result) {
|
||||||
if (result is Map<String, dynamic>) {
|
if (result is Map<String, dynamic>) {
|
||||||
|
@ -240,8 +232,7 @@ class ElectrumClient {
|
||||||
return <String, dynamic>{};
|
return <String, dynamic>{};
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<String> getTransactionHex(
|
Future<String> getTransactionHex({required String hash}) async =>
|
||||||
{required String hash}) async =>
|
|
||||||
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000)
|
callWithTimeout(method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000)
|
||||||
.then((dynamic result) {
|
.then((dynamic result) {
|
||||||
if (result is String) {
|
if (result is String) {
|
||||||
|
@ -252,29 +243,40 @@ class ElectrumClient {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<String> broadcastTransaction(
|
Future<String> broadcastTransaction(
|
||||||
{required String transactionRaw}) async =>
|
{required String transactionRaw, BasedUtxoNetwork? network}) async {
|
||||||
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
if (network == BitcoinNetwork.testnet) {
|
||||||
.then((dynamic result) {
|
return http
|
||||||
if (result is String) {
|
.post(Uri(scheme: 'https', host: 'blockstream.info', path: '/testnet/api/tx'),
|
||||||
return result;
|
headers: <String, String>{'Content-Type': 'application/json; charset=utf-8'},
|
||||||
|
body: transactionRaw)
|
||||||
|
.then((http.Response response) {
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return response.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
throw Exception('Failed to broadcast transaction: ${response.body}');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getMerkle(
|
return call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
||||||
{required String hash, required int height}) async =>
|
.then((dynamic result) {
|
||||||
await call(
|
if (result is String) {
|
||||||
method: 'blockchain.transaction.get_merkle',
|
return result;
|
||||||
params: [hash, height]) as Map<String, dynamic>;
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getHeader({required int height}) async =>
|
return '';
|
||||||
await call(method: 'blockchain.block.get_header', params: [height])
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getMerkle({required String hash, required int height}) async =>
|
||||||
|
await call(method: 'blockchain.transaction.get_merkle', params: [hash, height])
|
||||||
as Map<String, dynamic>;
|
as Map<String, dynamic>;
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getHeader({required int height}) async =>
|
||||||
|
await call(method: 'blockchain.block.get_header', params: [height]) as Map<String, dynamic>;
|
||||||
|
|
||||||
Future<double> estimatefee({required int p}) =>
|
Future<double> estimatefee({required int p}) =>
|
||||||
call(method: 'blockchain.estimatefee', params: [p])
|
call(method: 'blockchain.estimatefee', params: [p]).then((dynamic result) {
|
||||||
.then((dynamic result) {
|
|
||||||
if (result is double) {
|
if (result is double) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -314,20 +316,17 @@ class ElectrumClient {
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<List<int>> feeRates() async {
|
Future<List<int>> feeRates({BasedUtxoNetwork? network}) async {
|
||||||
|
if (network == BitcoinNetwork.testnet) {
|
||||||
|
return [1, 1, 1];
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
final topDoubleString = await estimatefee(p: 1);
|
final topDoubleString = await estimatefee(p: 1);
|
||||||
final middleDoubleString = await estimatefee(p: 5);
|
final middleDoubleString = await estimatefee(p: 5);
|
||||||
final bottomDoubleString = await estimatefee(p: 100);
|
final bottomDoubleString = await estimatefee(p: 100);
|
||||||
final top =
|
final top = (stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000).round();
|
||||||
(stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000)
|
final middle = (stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000).round();
|
||||||
.round();
|
final bottom = (stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000).round();
|
||||||
final middle =
|
|
||||||
(stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000)
|
|
||||||
.round();
|
|
||||||
final bottom =
|
|
||||||
(stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000)
|
|
||||||
.round();
|
|
||||||
|
|
||||||
return [bottom, middle, top];
|
return [bottom, middle, top];
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
@ -335,6 +334,21 @@ 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>? scripthashUpdate(String scripthash) {
|
BehaviorSubject<Object>? scripthashUpdate(String scripthash) {
|
||||||
_id += 1;
|
_id += 1;
|
||||||
return subscribe<Object>(
|
return subscribe<Object>(
|
||||||
|
@ -344,16 +358,14 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
BehaviorSubject<T>? subscribe<T>(
|
BehaviorSubject<T>? subscribe<T>(
|
||||||
{required String id,
|
{required String id, required String method, List<Object> params = const []}) {
|
||||||
required String method,
|
|
||||||
List<Object> params = const []}) {
|
|
||||||
try {
|
try {
|
||||||
final subscription = BehaviorSubject<T>();
|
final subscription = BehaviorSubject<T>();
|
||||||
_regisrySubscription(id, subscription);
|
_regisrySubscription(id, subscription);
|
||||||
socket!.write(jsonrpc(method: method, id: _id, params: params));
|
socket!.write(jsonrpc(method: method, id: _id, params: params));
|
||||||
|
|
||||||
return subscription;
|
return subscription;
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -370,9 +382,7 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> callWithTimeout(
|
Future<dynamic> callWithTimeout(
|
||||||
{required String method,
|
{required String method, List<Object> params = const [], int timeout = 4000}) async {
|
||||||
List<Object> params = const [],
|
|
||||||
int timeout = 4000}) async {
|
|
||||||
try {
|
try {
|
||||||
final completer = Completer<dynamic>();
|
final completer = Completer<dynamic>();
|
||||||
_id += 1;
|
_id += 1;
|
||||||
|
@ -386,7 +396,7 @@ class ElectrumClient {
|
||||||
});
|
});
|
||||||
|
|
||||||
return completer.future;
|
return completer.future;
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,8 +407,8 @@ class ElectrumClient {
|
||||||
onConnectionStatusChange = null;
|
onConnectionStatusChange = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registryTask(int id, Completer<dynamic> completer) => _tasks[id.toString()] =
|
void _registryTask(int id, Completer<dynamic> completer) =>
|
||||||
SocketTask(completer: completer, isSubscription: false);
|
_tasks[id.toString()] = SocketTask(completer: completer, isSubscription: false);
|
||||||
|
|
||||||
void _regisrySubscription(String id, BehaviorSubject<dynamic> subject) =>
|
void _regisrySubscription(String id, BehaviorSubject<dynamic> subject) =>
|
||||||
_tasks[id] = SocketTask(subject: subject, isSubscription: true);
|
_tasks[id] = SocketTask(subject: subject, isSubscription: true);
|
||||||
|
@ -419,8 +429,7 @@ class ElectrumClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _methodHandler(
|
void _methodHandler({required String method, required Map<String, dynamic> request}) {
|
||||||
{required String method, required Map<String, dynamic> request}) {
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case 'blockchain.scripthash.subscribe':
|
case 'blockchain.scripthash.subscribe':
|
||||||
final params = request['params'] as List<dynamic>;
|
final params = request['params'] as List<dynamic>;
|
||||||
|
@ -451,8 +460,8 @@ class ElectrumClient {
|
||||||
_methodHandler(method: method, request: response);
|
_methodHandler(method: method, request: response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id != null){
|
if (id != null) {
|
||||||
_finish(id, result);
|
_finish(id, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||||
import 'package:cw_bitcoin/address_from_output.dart';
|
import 'package:cw_bitcoin/address_from_output.dart';
|
||||||
|
@ -10,13 +11,12 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
class ElectrumTransactionBundle {
|
class ElectrumTransactionBundle {
|
||||||
ElectrumTransactionBundle(this.originalTransaction,
|
ElectrumTransactionBundle(this.originalTransaction,
|
||||||
{required this.ins,
|
{required this.ins, required this.confirmations, this.time, required this.height});
|
||||||
required this.confirmations,
|
final BtcTransaction originalTransaction;
|
||||||
this.time});
|
final List<BtcTransaction> ins;
|
||||||
final bitcoin.Transaction originalTransaction;
|
|
||||||
final List<bitcoin.Transaction> ins;
|
|
||||||
final int? time;
|
final int? time;
|
||||||
final int confirmations;
|
final int confirmations;
|
||||||
|
final int height;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ElectrumTransactionInfo extends TransactionInfo {
|
class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
|
@ -39,8 +39,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
this.confirmations = confirmations;
|
this.confirmations = confirmations;
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ElectrumTransactionInfo.fromElectrumVerbose(
|
factory ElectrumTransactionInfo.fromElectrumVerbose(Map<String, Object> obj, WalletType type,
|
||||||
Map<String, Object> obj, WalletType type,
|
|
||||||
{required List<BitcoinAddressRecord> addresses, required int height}) {
|
{required List<BitcoinAddressRecord> addresses, required int height}) {
|
||||||
final addressesSet = addresses.map((addr) => addr.address).toSet();
|
final addressesSet = addresses.map((addr) => addr.address).toSet();
|
||||||
final id = obj['txid'] as String;
|
final id = obj['txid'] as String;
|
||||||
|
@ -58,10 +57,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
for (dynamic vin in vins) {
|
for (dynamic vin in vins) {
|
||||||
final vout = vin['vout'] as int;
|
final vout = vin['vout'] as int;
|
||||||
final out = vin['tx']['vout'][vout] as Map;
|
final out = vin['tx']['vout'][vout] as Map;
|
||||||
final outAddresses =
|
final outAddresses = (out['scriptPubKey']['addresses'] as List<Object>?)?.toSet();
|
||||||
(out['scriptPubKey']['addresses'] as List<Object>?)?.toSet();
|
inputsAmount += stringDoubleToBitcoinAmount((out['value'] as double? ?? 0).toString());
|
||||||
inputsAmount +=
|
|
||||||
stringDoubleToBitcoinAmount((out['value'] as double? ?? 0).toString());
|
|
||||||
|
|
||||||
if (outAddresses?.intersection(addressesSet).isNotEmpty ?? false) {
|
if (outAddresses?.intersection(addressesSet).isNotEmpty ?? false) {
|
||||||
direction = TransactionDirection.outgoing;
|
direction = TransactionDirection.outgoing;
|
||||||
|
@ -69,11 +66,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (dynamic out in vout) {
|
for (dynamic out in vout) {
|
||||||
final outAddresses =
|
final outAddresses = out['scriptPubKey']['addresses'] as List<Object>? ?? [];
|
||||||
out['scriptPubKey']['addresses'] as List<Object>? ?? [];
|
|
||||||
final ntrs = outAddresses.toSet().intersection(addressesSet);
|
final ntrs = outAddresses.toSet().intersection(addressesSet);
|
||||||
final value = stringDoubleToBitcoinAmount(
|
final value = stringDoubleToBitcoinAmount((out['value'] as double? ?? 0.0).toString());
|
||||||
(out['value'] as double? ?? 0.0).toString());
|
|
||||||
totalOutAmount += value;
|
totalOutAmount += value;
|
||||||
|
|
||||||
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||
|
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||
|
||||||
|
@ -96,44 +91,50 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ElectrumTransactionInfo.fromElectrumBundle(
|
factory ElectrumTransactionInfo.fromElectrumBundle(
|
||||||
ElectrumTransactionBundle bundle,
|
ElectrumTransactionBundle bundle, WalletType type, BasedUtxoNetwork network,
|
||||||
WalletType type,
|
{required Set<String> addresses, required int height}) {
|
||||||
bitcoin.NetworkType networkType,
|
|
||||||
{required Set<String> addresses,
|
|
||||||
required int height}) {
|
|
||||||
final date = bundle.time != null
|
final date = bundle.time != null
|
||||||
? DateTime.fromMillisecondsSinceEpoch(bundle.time! * 1000)
|
? DateTime.fromMillisecondsSinceEpoch(bundle.time! * 1000)
|
||||||
: DateTime.now();
|
: DateTime.now();
|
||||||
var direction = TransactionDirection.incoming;
|
var direction = TransactionDirection.incoming;
|
||||||
var amount = 0;
|
var amount = 0;
|
||||||
var inputAmount = 0;
|
var inputAmount = 0;
|
||||||
var totalOutAmount = 0;
|
var totalOutAmount = 0;
|
||||||
|
|
||||||
for (var i = 0; i < bundle.originalTransaction.ins.length; i++) {
|
for (var i = 0; i < bundle.originalTransaction.inputs.length; i++) {
|
||||||
final input = bundle.originalTransaction.ins[i];
|
final input = bundle.originalTransaction.inputs[i];
|
||||||
final inputTransaction = bundle.ins[i];
|
final inputTransaction = bundle.ins[i];
|
||||||
final vout = input.index;
|
final outTransaction = inputTransaction.outputs[input.txIndex];
|
||||||
final outTransaction = inputTransaction.outs[vout!];
|
inputAmount += outTransaction.amount.toInt();
|
||||||
final address = addressFromOutput(outTransaction.script!, networkType);
|
if (addresses.contains(addressFromOutputScript(outTransaction.scriptPubKey, network))) {
|
||||||
inputAmount += outTransaction.value!;
|
|
||||||
if (addresses.contains(address)) {
|
|
||||||
direction = TransactionDirection.outgoing;
|
direction = TransactionDirection.outgoing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final out in bundle.originalTransaction.outs) {
|
final receivedAmounts = <int>[];
|
||||||
totalOutAmount += out.value!;
|
for (final out in bundle.originalTransaction.outputs) {
|
||||||
final address = addressFromOutput(out.script!, networkType);
|
totalOutAmount += out.amount.toInt();
|
||||||
final addressExists = addresses.contains(address);
|
final addressExists = addresses.contains(addressFromOutputScript(out.scriptPubKey, network));
|
||||||
|
|
||||||
|
if (addressExists) {
|
||||||
|
receivedAmounts.add(out.amount.toInt());
|
||||||
|
}
|
||||||
|
|
||||||
if ((direction == TransactionDirection.incoming && addressExists) ||
|
if ((direction == TransactionDirection.incoming && addressExists) ||
|
||||||
(direction == TransactionDirection.outgoing && !addressExists)) {
|
(direction == TransactionDirection.outgoing && !addressExists)) {
|
||||||
amount += out.value!;
|
amount += out.amount.toInt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (receivedAmounts.length == bundle.originalTransaction.outputs.length) {
|
||||||
|
// Self-send
|
||||||
|
direction = TransactionDirection.incoming;
|
||||||
|
amount = receivedAmounts.reduce((a, b) => a + b);
|
||||||
|
}
|
||||||
|
|
||||||
final fee = inputAmount - totalOutAmount;
|
final fee = inputAmount - totalOutAmount;
|
||||||
return ElectrumTransactionInfo(type,
|
return ElectrumTransactionInfo(type,
|
||||||
id: bundle.originalTransaction.getId(),
|
id: bundle.originalTransaction.txId(),
|
||||||
height: height,
|
height: height,
|
||||||
isPending: bundle.confirmations == 0,
|
isPending: bundle.confirmations == 0,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
|
@ -152,8 +153,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
if (addresses != null) {
|
if (addresses != null) {
|
||||||
tx.outs.forEach((out) {
|
tx.outs.forEach((out) {
|
||||||
try {
|
try {
|
||||||
final p2pkh = bitcoin.P2PKH(
|
final p2pkh =
|
||||||
data: PaymentData(output: out.script), network: bitcoin.bitcoin);
|
bitcoin.P2PKH(data: PaymentData(output: out.script), network: bitcoin.bitcoin);
|
||||||
exist = addresses.contains(p2pkh.data.address);
|
exist = addresses.contains(p2pkh.data.address);
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
|
@ -163,9 +164,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
final date = timestamp != null
|
final date =
|
||||||
? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000)
|
timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000) : DateTime.now();
|
||||||
: DateTime.now();
|
|
||||||
|
|
||||||
return ElectrumTransactionInfo(type,
|
return ElectrumTransactionInfo(type,
|
||||||
id: tx.getId(),
|
id: tx.getId(),
|
||||||
|
@ -178,8 +178,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||||
confirmations: confirmations);
|
confirmations: confirmations);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ElectrumTransactionInfo.fromJson(
|
factory ElectrumTransactionInfo.fromJson(Map<String, dynamic> data, WalletType type) {
|
||||||
Map<String, dynamic> data, WalletType type) {
|
|
||||||
return ElectrumTransactionInfo(type,
|
return ElectrumTransactionInfo(type,
|
||||||
id: data['id'] as String,
|
id: data['id'] as String,
|
||||||
height: data['height'] as int,
|
height: data['height'] as int,
|
||||||
|
|
|
@ -3,9 +3,10 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin_base;
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cw_bitcoin/address_to_output_script.dart';
|
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart';
|
||||||
|
@ -18,6 +19,7 @@ import 'package:cw_bitcoin/electrum_balance.dart';
|
||||||
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
||||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||||
|
import 'package:cw_bitcoin/litecoin_network.dart';
|
||||||
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
||||||
import 'package:cw_bitcoin/script_hash.dart';
|
import 'package:cw_bitcoin/script_hash.dart';
|
||||||
import 'package:cw_bitcoin/utils.dart';
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
|
@ -37,6 +39,7 @@ import 'package:hex/hex.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:rxdart/subjects.dart';
|
import 'package:rxdart/subjects.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
part 'electrum_wallet.g.dart';
|
part 'electrum_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -73,6 +76,12 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
this.unspentCoinsInfo = unspentCoinsInfo,
|
this.unspentCoinsInfo = unspentCoinsInfo,
|
||||||
|
this.network = networkType == bitcoin.bitcoin
|
||||||
|
? BitcoinNetwork.mainnet
|
||||||
|
: networkType == litecoinNetwork
|
||||||
|
? LitecoinNetwork.mainnet
|
||||||
|
: BitcoinNetwork.testnet,
|
||||||
|
this.isTestnet = networkType == bitcoin.testnet,
|
||||||
super(walletInfo) {
|
super(walletInfo) {
|
||||||
this.electrumClient = electrumClient ?? ElectrumClient();
|
this.electrumClient = electrumClient ?? ElectrumClient();
|
||||||
this.walletInfo = walletInfo;
|
this.walletInfo = walletInfo;
|
||||||
|
@ -106,13 +115,13 @@ abstract class ElectrumWalletBase
|
||||||
@observable
|
@observable
|
||||||
SyncStatus syncStatus;
|
SyncStatus syncStatus;
|
||||||
|
|
||||||
List<String> get scriptHashes => walletAddresses.addresses
|
List<String> get scriptHashes => walletAddresses.addressesByReceiveType
|
||||||
.map((addr) => scriptHash(addr.address, networkType: networkType))
|
.map((addr) => scriptHash(addr.address, network: network))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
List<String> get publicScriptHashes => walletAddresses.addresses
|
List<String> get publicScriptHashes => walletAddresses.allAddresses
|
||||||
.where((addr) => !addr.isHidden)
|
.where((addr) => !addr.isHidden)
|
||||||
.map((addr) => scriptHash(addr.address, networkType: networkType))
|
.map((addr) => scriptHash(addr.address, network: network))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
String get xpub => hd.base58!;
|
String get xpub => hd.base58!;
|
||||||
|
@ -121,6 +130,10 @@ abstract class ElectrumWalletBase
|
||||||
String get seed => mnemonic;
|
String get seed => mnemonic;
|
||||||
|
|
||||||
bitcoin.NetworkType networkType;
|
bitcoin.NetworkType networkType;
|
||||||
|
BasedUtxoNetwork network;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool? isTestnet;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BitcoinWalletKeys get keys =>
|
BitcoinWalletKeys get keys =>
|
||||||
|
@ -145,12 +158,11 @@ abstract class ElectrumWalletBase
|
||||||
Future<void> startSync() async {
|
Future<void> startSync() async {
|
||||||
try {
|
try {
|
||||||
syncStatus = AttemptingSyncStatus();
|
syncStatus = AttemptingSyncStatus();
|
||||||
await walletAddresses.discoverAddresses();
|
|
||||||
await updateTransactions();
|
await updateTransactions();
|
||||||
_subscribeForUpdates();
|
_subscribeForUpdates();
|
||||||
await updateUnspent();
|
await updateUnspent();
|
||||||
await updateBalance();
|
await updateBalance();
|
||||||
_feeRates = await electrumClient.feeRates();
|
_feeRates = await electrumClient.feeRates(network: network);
|
||||||
|
|
||||||
Timer.periodic(
|
Timer.periodic(
|
||||||
const Duration(minutes: 1), (timer) async => _feeRates = await electrumClient.feeRates());
|
const Duration(minutes: 1), (timer) async => _feeRates = await electrumClient.feeRates());
|
||||||
|
@ -181,183 +193,206 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Future<EstimatedTxResult> _estimateTxFeeAndInputsToUse(
|
||||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
int credentialsAmount,
|
||||||
const minAmount = 546;
|
bool sendAll,
|
||||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
List<BitcoinBaseAddress> outputAddresses,
|
||||||
final inputs = <BitcoinUnspent>[];
|
List<BitcoinOutput> outputs,
|
||||||
final outputs = transactionCredentials.outputs;
|
BitcoinTransactionCredentials transactionCredentials,
|
||||||
final hasMultiDestination = outputs.length > 1;
|
{int? inputsCount}) async {
|
||||||
|
final utxos = <UtxoWithAddress>[];
|
||||||
|
List<ECPrivate> privateKeys = [];
|
||||||
|
|
||||||
|
var leftAmount = credentialsAmount;
|
||||||
var allInputsAmount = 0;
|
var allInputsAmount = 0;
|
||||||
|
|
||||||
if (unspentCoins.isEmpty) {
|
for (int i = 0; i < unspentCoins.length; i++) {
|
||||||
await updateUnspent();
|
final utx = unspentCoins[i];
|
||||||
}
|
|
||||||
|
|
||||||
for (final utx in unspentCoins) {
|
|
||||||
if (utx.isSending) {
|
if (utx.isSending) {
|
||||||
allInputsAmount += utx.value;
|
allInputsAmount += utx.value;
|
||||||
inputs.add(utx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputs.isEmpty) {
|
|
||||||
throw BitcoinTransactionNoInputsException();
|
|
||||||
}
|
|
||||||
|
|
||||||
final allAmountFee = transactionCredentials.feeRate != null
|
|
||||||
? feeAmountWithFeeRate(transactionCredentials.feeRate!, inputs.length, outputs.length)
|
|
||||||
: feeAmountForPriority(transactionCredentials.priority!, inputs.length, outputs.length);
|
|
||||||
|
|
||||||
final allAmount = allInputsAmount - allAmountFee;
|
|
||||||
|
|
||||||
var credentialsAmount = 0;
|
|
||||||
var amount = 0;
|
|
||||||
var fee = 0;
|
|
||||||
|
|
||||||
if (hasMultiDestination) {
|
|
||||||
if (outputs.any((item) => item.sendAll || item.formattedCryptoAmount! <= 0)) {
|
|
||||||
throw BitcoinTransactionWrongBalanceException(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
credentialsAmount = outputs.fold(0, (acc, value) => acc + value.formattedCryptoAmount!);
|
|
||||||
|
|
||||||
if (allAmount - credentialsAmount < minAmount) {
|
|
||||||
throw BitcoinTransactionWrongBalanceException(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
amount = credentialsAmount;
|
|
||||||
|
|
||||||
if (transactionCredentials.feeRate != null) {
|
|
||||||
fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount,
|
|
||||||
outputsCount: outputs.length + 1);
|
|
||||||
} else {
|
|
||||||
fee = calculateEstimatedFee(transactionCredentials.priority, amount,
|
|
||||||
outputsCount: outputs.length + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final output = outputs.first;
|
|
||||||
credentialsAmount = !output.sendAll ? output.formattedCryptoAmount! : 0;
|
|
||||||
|
|
||||||
if (credentialsAmount > allAmount) {
|
|
||||||
throw BitcoinTransactionWrongBalanceException(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
amount = output.sendAll || allAmount - credentialsAmount < minAmount
|
|
||||||
? allAmount
|
|
||||||
: credentialsAmount;
|
|
||||||
|
|
||||||
if (output.sendAll || amount == allAmount) {
|
|
||||||
fee = allAmountFee;
|
|
||||||
} else if (transactionCredentials.feeRate != null) {
|
|
||||||
fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount);
|
|
||||||
} else {
|
|
||||||
fee = calculateEstimatedFee(transactionCredentials.priority, amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fee == 0) {
|
|
||||||
throw BitcoinTransactionWrongBalanceException(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
final totalAmount = amount + fee;
|
|
||||||
|
|
||||||
if (totalAmount > balance[currency]!.confirmed || totalAmount > allInputsAmount) {
|
|
||||||
throw BitcoinTransactionWrongBalanceException(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
final txb = bitcoin.TransactionBuilder(network: networkType);
|
|
||||||
final changeAddress = await walletAddresses.getChangeAddress();
|
|
||||||
var leftAmount = totalAmount;
|
|
||||||
var totalInputAmount = 0;
|
|
||||||
|
|
||||||
inputs.clear();
|
|
||||||
|
|
||||||
for (final utx in unspentCoins) {
|
|
||||||
if (utx.isSending) {
|
|
||||||
leftAmount = leftAmount - utx.value;
|
leftAmount = leftAmount - utx.value;
|
||||||
totalInputAmount += utx.value;
|
|
||||||
inputs.add(utx);
|
|
||||||
|
|
||||||
if (leftAmount <= 0) {
|
final address = _addressTypeFromStr(utx.address, network);
|
||||||
|
final privkey = generateECPrivate(
|
||||||
|
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
||||||
|
index: utx.bitcoinAddressRecord.index,
|
||||||
|
network: network);
|
||||||
|
|
||||||
|
privateKeys.add(privkey);
|
||||||
|
|
||||||
|
utxos.add(
|
||||||
|
UtxoWithAddress(
|
||||||
|
utxo: BitcoinUtxo(
|
||||||
|
txHash: utx.hash,
|
||||||
|
value: BigInt.from(utx.value),
|
||||||
|
vout: utx.vout,
|
||||||
|
scriptType: _getScriptType(address),
|
||||||
|
),
|
||||||
|
ownerDetails:
|
||||||
|
UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
bool amountIsAcquired = !sendAll && leftAmount <= 0;
|
||||||
|
if ((inputsCount == null && amountIsAcquired) || inputsCount == i + 1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputs.isEmpty) {
|
if (utxos.isEmpty) {
|
||||||
throw BitcoinTransactionNoInputsException();
|
throw BitcoinTransactionNoInputsException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount <= 0 || totalInputAmount < totalAmount) {
|
var changeValue = allInputsAmount - credentialsAmount;
|
||||||
|
|
||||||
|
if (!sendAll) {
|
||||||
|
if (changeValue > 0) {
|
||||||
|
final changeAddress = await walletAddresses.getChangeAddress();
|
||||||
|
final address = _addressTypeFromStr(changeAddress, network);
|
||||||
|
outputAddresses.add(address);
|
||||||
|
outputs.add(BitcoinOutput(address: address, value: BigInt.from(changeValue)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize(
|
||||||
|
utxos: utxos, outputs: outputs, network: network);
|
||||||
|
|
||||||
|
final fee = transactionCredentials.feeRate != null
|
||||||
|
? feeAmountWithFeeRate(transactionCredentials.feeRate!, 0, 0, size: estimatedSize)
|
||||||
|
: feeAmountForPriority(transactionCredentials.priority!, 0, 0, size: estimatedSize);
|
||||||
|
|
||||||
|
if (fee == 0) {
|
||||||
throw BitcoinTransactionWrongBalanceException(currency);
|
throw BitcoinTransactionWrongBalanceException(currency);
|
||||||
}
|
}
|
||||||
|
|
||||||
txb.setVersion(1);
|
var amount = credentialsAmount;
|
||||||
inputs.forEach((input) {
|
|
||||||
if (input.isP2wpkh) {
|
|
||||||
final p2wpkh = bitcoin
|
|
||||||
.P2WPKH(
|
|
||||||
data: generatePaymentData(
|
|
||||||
hd: input.bitcoinAddressRecord.isHidden
|
|
||||||
? walletAddresses.sideHd
|
|
||||||
: walletAddresses.mainHd,
|
|
||||||
index: input.bitcoinAddressRecord.index),
|
|
||||||
network: networkType)
|
|
||||||
.data;
|
|
||||||
|
|
||||||
txb.addInput(input.hash, input.vout, null, p2wpkh.output);
|
final lastOutput = outputs.last;
|
||||||
} else {
|
if (!sendAll) {
|
||||||
txb.addInput(input.hash, input.vout);
|
if (changeValue > fee) {
|
||||||
|
// Here, lastOutput is change, deduct the fee from it
|
||||||
|
outputs[outputs.length - 1] =
|
||||||
|
BitcoinOutput(address: lastOutput.address, value: lastOutput.value - BigInt.from(fee));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
outputs.forEach((item) {
|
|
||||||
final outputAmount = hasMultiDestination ? item.formattedCryptoAmount : amount;
|
|
||||||
final outputAddress = item.isParsedAddress ? item.extractedAddress! : item.address;
|
|
||||||
txb.addOutput(addressToOutputScript(outputAddress, networkType), outputAmount!);
|
|
||||||
});
|
|
||||||
|
|
||||||
final estimatedSize = estimatedTransactionSize(inputs.length, outputs.length + 1);
|
|
||||||
var feeAmount = 0;
|
|
||||||
|
|
||||||
if (transactionCredentials.feeRate != null) {
|
|
||||||
feeAmount = transactionCredentials.feeRate! * estimatedSize;
|
|
||||||
} else {
|
} else {
|
||||||
feeAmount = feeRate(transactionCredentials.priority!) * estimatedSize;
|
// Here, if sendAll, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount for change
|
||||||
|
amount = allInputsAmount - fee;
|
||||||
|
outputs[outputs.length - 1] =
|
||||||
|
BitcoinOutput(address: lastOutput.address, value: BigInt.from(amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
final changeValue = totalInputAmount - amount - feeAmount;
|
final totalAmount = amount + fee;
|
||||||
|
|
||||||
if (changeValue > minAmount) {
|
if (totalAmount > balance[currency]!.confirmed) {
|
||||||
txb.addOutput(changeAddress, changeValue);
|
throw BitcoinTransactionWrongBalanceException(currency);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
if (totalAmount > allInputsAmount) {
|
||||||
final input = inputs[i];
|
if (unspentCoins.where((utx) => utx.isSending).length == utxos.length) {
|
||||||
final keyPair = generateKeyPair(
|
throw BitcoinTransactionWrongBalanceException(currency);
|
||||||
hd: input.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
} else {
|
||||||
index: input.bitcoinAddressRecord.index,
|
if (changeValue > fee) {
|
||||||
network: networkType);
|
outputAddresses.removeLast();
|
||||||
final witnessValue = input.isP2wpkh ? input.value : null;
|
outputs.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
txb.sign(vin: i, keyPair: keyPair, witnessValue: witnessValue);
|
return _estimateTxFeeAndInputsToUse(
|
||||||
|
credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials,
|
||||||
|
inputsCount: utxos.length + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PendingBitcoinTransaction(txb.build(), type,
|
return EstimatedTxResult(utxos: utxos, privateKeys: privateKeys, fee: fee, amount: amount);
|
||||||
electrumClient: electrumClient, amount: amount, fee: fee)
|
}
|
||||||
..addListener((transaction) async {
|
|
||||||
transactionHistory.addOne(transaction);
|
@override
|
||||||
await updateBalance();
|
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||||
|
try {
|
||||||
|
final outputs = <BitcoinOutput>[];
|
||||||
|
final outputAddresses = <BitcoinBaseAddress>[];
|
||||||
|
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||||
|
final hasMultiDestination = transactionCredentials.outputs.length > 1;
|
||||||
|
final sendAll = !hasMultiDestination && transactionCredentials.outputs.first.sendAll;
|
||||||
|
|
||||||
|
var credentialsAmount = 0;
|
||||||
|
|
||||||
|
for (final out in transactionCredentials.outputs) {
|
||||||
|
final outputAddress = out.isParsedAddress ? out.extractedAddress! : out.address;
|
||||||
|
final address = _addressTypeFromStr(outputAddress, network);
|
||||||
|
|
||||||
|
outputAddresses.add(address);
|
||||||
|
|
||||||
|
if (hasMultiDestination) {
|
||||||
|
if (out.sendAll || out.formattedCryptoAmount! <= 0) {
|
||||||
|
throw BitcoinTransactionWrongBalanceException(currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
final outputAmount = out.formattedCryptoAmount!;
|
||||||
|
credentialsAmount += outputAmount;
|
||||||
|
|
||||||
|
outputs.add(BitcoinOutput(address: address, value: BigInt.from(outputAmount)));
|
||||||
|
} else {
|
||||||
|
if (!sendAll) {
|
||||||
|
final outputAmount = out.formattedCryptoAmount!;
|
||||||
|
credentialsAmount += outputAmount;
|
||||||
|
outputs.add(BitcoinOutput(address: address, value: BigInt.from(outputAmount)));
|
||||||
|
} else {
|
||||||
|
// The value will be changed after estimating the Tx size and deducting the fee from the total
|
||||||
|
outputs.add(BitcoinOutput(address: address, value: BigInt.from(0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final estimatedTx = await _estimateTxFeeAndInputsToUse(
|
||||||
|
credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials);
|
||||||
|
|
||||||
|
final txb = BitcoinTransactionBuilder(
|
||||||
|
utxos: estimatedTx.utxos,
|
||||||
|
outputs: outputs,
|
||||||
|
fee: BigInt.from(estimatedTx.fee),
|
||||||
|
network: network);
|
||||||
|
|
||||||
|
final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) {
|
||||||
|
final key = estimatedTx.privateKeys
|
||||||
|
.firstWhereOrNull((element) => element.getPublic().toHex() == publicKey);
|
||||||
|
|
||||||
|
if (key == null) {
|
||||||
|
throw Exception("Cannot find private key");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utxo.utxo.isP2tr()) {
|
||||||
|
return key.signTapRoot(txDigest, sighash: sighash);
|
||||||
|
} else {
|
||||||
|
return key.signInput(txDigest, sigHash: sighash);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return PendingBitcoinTransaction(transaction, type,
|
||||||
|
electrumClient: electrumClient,
|
||||||
|
amount: estimatedTx.amount,
|
||||||
|
fee: estimatedTx.fee,
|
||||||
|
network: network)
|
||||||
|
..addListener((transaction) async {
|
||||||
|
transactionHistory.addOne(transaction);
|
||||||
|
await updateBalance();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String toJSON() => json.encode({
|
String toJSON() => json.encode({
|
||||||
'mnemonic': mnemonic,
|
'mnemonic': mnemonic,
|
||||||
'account_index': walletAddresses.currentReceiveAddressIndex.toString(),
|
'account_index': walletAddresses.currentReceiveAddressIndexByType,
|
||||||
'change_address_index': walletAddresses.currentChangeAddressIndex.toString(),
|
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
||||||
'addresses': walletAddresses.addresses.map((addr) => addr.toJSON()).toList(),
|
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
|
||||||
'balance': balance[currency]?.toJSON()
|
'address_page_type': walletInfo.addressPageType == null
|
||||||
|
? SegwitAddresType.p2wpkh.toString()
|
||||||
|
: walletInfo.addressPageType.toString(),
|
||||||
|
'balance': balance[currency]?.toJSON(),
|
||||||
|
'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet',
|
||||||
});
|
});
|
||||||
|
|
||||||
int feeRate(TransactionPriority priority) {
|
int feeRate(TransactionPriority priority) {
|
||||||
|
@ -372,24 +407,29 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int feeAmountForPriority(
|
int feeAmountForPriority(BitcoinTransactionPriority priority, int inputsCount, int outputsCount,
|
||||||
BitcoinTransactionPriority priority, int inputsCount, int outputsCount) =>
|
{int? size}) =>
|
||||||
feeRate(priority) * estimatedTransactionSize(inputsCount, outputsCount);
|
feeRate(priority) * (size ?? estimatedTransactionSize(inputsCount, outputsCount));
|
||||||
|
|
||||||
int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount) =>
|
int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount, {int? size}) =>
|
||||||
feeRate * estimatedTransactionSize(inputsCount, outputsCount);
|
feeRate * (size ?? estimatedTransactionSize(inputsCount, outputsCount));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int calculateEstimatedFee(TransactionPriority? priority, int? amount, {int? outputsCount}) {
|
int calculateEstimatedFee(TransactionPriority? priority, int? amount,
|
||||||
|
{int? outputsCount, int? size}) {
|
||||||
if (priority is BitcoinTransactionPriority) {
|
if (priority is BitcoinTransactionPriority) {
|
||||||
return calculateEstimatedFeeWithFeeRate(feeRate(priority), amount,
|
return calculateEstimatedFeeWithFeeRate(feeRate(priority), amount,
|
||||||
outputsCount: outputsCount);
|
outputsCount: outputsCount, size: size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount}) {
|
int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount, int? size}) {
|
||||||
|
if (size != null) {
|
||||||
|
return feeAmountWithFeeRate(feeRate, 0, 0, size: size);
|
||||||
|
}
|
||||||
|
|
||||||
int inputsCount = 0;
|
int inputsCount = 0;
|
||||||
|
|
||||||
if (amount != null) {
|
if (amount != null) {
|
||||||
|
@ -457,9 +497,6 @@ abstract class ElectrumWalletBase
|
||||||
await transactionHistory.changePassword(password);
|
await transactionHistory.changePassword(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
bitcoin.ECPair keyPairFor({required int index}) =>
|
|
||||||
generateKeyPair(hd: hd, index: index, network: networkType);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rescan({required int height}) async => throw UnimplementedError();
|
Future<void> rescan({required int height}) async => throw UnimplementedError();
|
||||||
|
|
||||||
|
@ -473,20 +510,23 @@ abstract class ElectrumWalletBase
|
||||||
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
|
||||||
Future<void> updateUnspent() async {
|
Future<void> updateUnspent() async {
|
||||||
final unspent = await Future.wait(walletAddresses.addresses.map((address) => electrumClient
|
List<BitcoinUnspent> updatedUnspentCoins = [];
|
||||||
.getListUnspentWithAddress(address.address, networkType)
|
|
||||||
.then((unspent) => unspent.map((unspent) {
|
final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet();
|
||||||
|
|
||||||
|
await Future.wait(walletAddresses.allAddresses.map((address) => electrumClient
|
||||||
|
.getListUnspentWithAddress(address.address, network)
|
||||||
|
.then((unspent) => Future.forEach<Map<String, dynamic>>(unspent, (unspent) async {
|
||||||
try {
|
try {
|
||||||
return BitcoinUnspent.fromJSON(address, unspent);
|
final coin = BitcoinUnspent.fromJSON(address, unspent);
|
||||||
} catch (_) {
|
final tx = await fetchTransactionInfo(
|
||||||
return null;
|
hash: coin.hash, height: 0, myAddresses: addressesSet);
|
||||||
}
|
coin.isChange = tx?.direction == TransactionDirection.outgoing;
|
||||||
}).whereNotNull())));
|
updatedUnspentCoins.add(coin);
|
||||||
unspentCoins = unspent.expand((e) => e).toList();
|
} catch (_) {}
|
||||||
unspentCoins.forEach((coin) async {
|
}))));
|
||||||
final tx = await fetchTransactionInfo(hash: coin.hash, height: 0);
|
|
||||||
coin.isChange = tx?.direction == TransactionDirection.outgoing;
|
unspentCoins = updatedUnspentCoins;
|
||||||
});
|
|
||||||
|
|
||||||
if (unspentCoinsInfo.isEmpty) {
|
if (unspentCoinsInfo.isEmpty) {
|
||||||
unspentCoins.forEach((coin) => _addCoinInfo(coin));
|
unspentCoins.forEach((coin) => _addCoinInfo(coin));
|
||||||
|
@ -495,8 +535,10 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
if (unspentCoins.isNotEmpty) {
|
if (unspentCoins.isNotEmpty) {
|
||||||
unspentCoins.forEach((coin) {
|
unspentCoins.forEach((coin) {
|
||||||
final coinInfoList = unspentCoinsInfo.values
|
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
||||||
.where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash));
|
element.walletId.contains(id) &&
|
||||||
|
element.hash.contains(coin.hash) &&
|
||||||
|
element.vout == coin.vout);
|
||||||
|
|
||||||
if (coinInfoList.isNotEmpty) {
|
if (coinInfoList.isNotEmpty) {
|
||||||
final coinInfo = coinInfoList.first;
|
final coinInfo = coinInfoList.first;
|
||||||
|
@ -537,7 +579,8 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
if (currentWalletUnspentCoins.isNotEmpty) {
|
if (currentWalletUnspentCoins.isNotEmpty) {
|
||||||
currentWalletUnspentCoins.forEach((element) {
|
currentWalletUnspentCoins.forEach((element) {
|
||||||
final existUnspentCoins = unspentCoins.where((coin) => element.hash.contains(coin.hash));
|
final existUnspentCoins = unspentCoins
|
||||||
|
.where((coin) => element.hash.contains(coin.hash) && element.vout == coin.vout);
|
||||||
|
|
||||||
if (existUnspentCoins.isEmpty) {
|
if (existUnspentCoins.isEmpty) {
|
||||||
keys.add(element.key);
|
keys.add(element.key);
|
||||||
|
@ -555,92 +598,152 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
Future<ElectrumTransactionBundle> getTransactionExpanded(
|
Future<ElectrumTransactionBundle> getTransactionExpanded(
|
||||||
{required String hash, required int height}) async {
|
{required String hash, required int height}) async {
|
||||||
final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
|
String transactionHex;
|
||||||
final transactionHex = verboseTransaction['hex'] as String;
|
int? time;
|
||||||
final original = bitcoin.Transaction.fromHex(transactionHex);
|
int confirmations = 0;
|
||||||
final ins = <bitcoin.Transaction>[];
|
if (network == BitcoinNetwork.testnet) {
|
||||||
final time = verboseTransaction['time'] as int?;
|
// Testnet public electrum server does not support verbose transaction fetching
|
||||||
final confirmations = verboseTransaction['confirmations'] as int? ?? 0;
|
transactionHex = await electrumClient.getTransactionHex(hash: hash);
|
||||||
|
|
||||||
for (final vin in original.ins) {
|
final status = json.decode(
|
||||||
final id = HEX.encode(vin.hash!.reversed.toList());
|
(await http.get(Uri.parse("https://blockstream.info/testnet/api/tx/$hash/status"))).body);
|
||||||
final txHex = await electrumClient.getTransactionHex(hash: id);
|
|
||||||
final tx = bitcoin.Transaction.fromHex(txHex);
|
time = status["block_time"] as int?;
|
||||||
ins.add(tx);
|
final tip = await electrumClient.getCurrentBlockChainTip() ?? 0;
|
||||||
|
confirmations = tip - (status["block_height"] as int? ?? 0);
|
||||||
|
} else {
|
||||||
|
final verboseTransaction = await electrumClient.getTransactionRaw(hash: hash);
|
||||||
|
|
||||||
|
transactionHex = verboseTransaction['hex'] as String;
|
||||||
|
time = verboseTransaction['time'] as int?;
|
||||||
|
confirmations = verboseTransaction['confirmations'] as int? ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ElectrumTransactionBundle(original, ins: ins, time: time, confirmations: confirmations);
|
final original = bitcoin_base.BtcTransaction.fromRaw(transactionHex);
|
||||||
|
final ins = <bitcoin_base.BtcTransaction>[];
|
||||||
|
|
||||||
|
for (final vin in original.inputs) {
|
||||||
|
try {
|
||||||
|
final id = HEX.encode(HEX.decode(vin.txId).reversed.toList());
|
||||||
|
final txHex = await electrumClient.getTransactionHex(hash: id);
|
||||||
|
final tx = bitcoin_base.BtcTransaction.fromRaw(txHex);
|
||||||
|
ins.add(tx);
|
||||||
|
} catch (_) {
|
||||||
|
ins.add(bitcoin_base.BtcTransaction.fromRaw(
|
||||||
|
await electrumClient.getTransactionHex(hash: vin.txId),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElectrumTransactionBundle(original,
|
||||||
|
ins: ins, time: time, confirmations: confirmations, height: height);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ElectrumTransactionInfo?> fetchTransactionInfo(
|
Future<ElectrumTransactionInfo?> fetchTransactionInfo(
|
||||||
{required String hash, required int height}) async {
|
{required String hash,
|
||||||
|
required int height,
|
||||||
|
required Set<String> myAddresses,
|
||||||
|
bool? retryOnFailure}) async {
|
||||||
try {
|
try {
|
||||||
final tx = await getTransactionExpanded(hash: hash, height: height);
|
return ElectrumTransactionInfo.fromElectrumBundle(
|
||||||
final addresses = walletAddresses.addresses.map((addr) => addr.address).toSet();
|
await getTransactionExpanded(hash: hash, height: height), walletInfo.type, network,
|
||||||
return ElectrumTransactionInfo.fromElectrumBundle(tx, walletInfo.type, networkType,
|
addresses: myAddresses, height: height);
|
||||||
addresses: addresses, height: height);
|
} catch (e) {
|
||||||
} catch (_) {
|
if (e is FormatException && retryOnFailure == true) {
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
return fetchTransactionInfo(hash: hash, height: height, myAddresses: myAddresses);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
|
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
|
||||||
final addressHashes = <String, BitcoinAddressRecord>{};
|
|
||||||
final normalizedHistories = <Map<String, dynamic>>[];
|
|
||||||
final newTxCounts = <String, int>{};
|
|
||||||
|
|
||||||
walletAddresses.addresses.forEach((addressRecord) {
|
|
||||||
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
|
||||||
addressHashes[sh] = addressRecord;
|
|
||||||
newTxCounts[sh] = 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final histories = addressHashes.keys.map((scriptHash) =>
|
final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};
|
||||||
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
|
final addressesSet = walletAddresses.allAddresses.map((addr) => addr.address).toSet();
|
||||||
final historyResults = await Future.wait(histories);
|
final currentHeight = await electrumClient.getCurrentBlockChainTip() ?? 0;
|
||||||
|
|
||||||
historyResults.forEach((history) {
|
await Future.wait(ADDRESS_TYPES.map((type) {
|
||||||
history.entries.forEach((historyItem) {
|
final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type);
|
||||||
if (historyItem.value.isNotEmpty) {
|
|
||||||
final address = addressHashes[historyItem.key];
|
return Future.wait(addressesByType.map((addressRecord) async {
|
||||||
address?.setAsUsed();
|
final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight);
|
||||||
newTxCounts[historyItem.key] = historyItem.value.length;
|
|
||||||
normalizedHistories.addAll(historyItem.value);
|
if (history.isNotEmpty) {
|
||||||
|
addressRecord.txCount = history.length;
|
||||||
|
historiesWithDetails.addAll(history);
|
||||||
|
|
||||||
|
final matchedAddresses =
|
||||||
|
addressesByType.where((addr) => addr.isHidden == addressRecord.isHidden);
|
||||||
|
|
||||||
|
final isLastUsedAddress =
|
||||||
|
history.isNotEmpty && addressRecord.address == matchedAddresses.last.address;
|
||||||
|
|
||||||
|
if (isLastUsedAddress) {
|
||||||
|
await walletAddresses.discoverAddresses(
|
||||||
|
matchedAddresses.toList(),
|
||||||
|
addressRecord.isHidden,
|
||||||
|
(address, addressesSet) =>
|
||||||
|
_fetchAddressHistory(address, addressesSet, currentHeight)
|
||||||
|
.then((history) => history.isNotEmpty ? address.address : null),
|
||||||
|
type: type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
for (var sh in addressHashes.keys) {
|
|
||||||
var balanceData = await electrumClient.getBalance(sh);
|
|
||||||
var addressRecord = addressHashes[sh];
|
|
||||||
if (addressRecord != null) {
|
|
||||||
addressRecord.balance = balanceData['confirmed'] as int? ?? 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addressHashes.forEach((sh, addressRecord) {
|
|
||||||
addressRecord.txCount = newTxCounts[sh] ?? 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
final historiesWithDetails = await Future.wait(normalizedHistories.map((transaction) {
|
|
||||||
try {
|
|
||||||
return fetchTransactionInfo(
|
|
||||||
hash: transaction['tx_hash'] as String, height: transaction['height'] as int);
|
|
||||||
} catch (_) {
|
|
||||||
return Future.value(null);
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>(
|
return historiesWithDetails;
|
||||||
<String, ElectrumTransactionInfo>{}, (acc, tx) {
|
} catch (e) {
|
||||||
if (tx == null) {
|
print(e.toString());
|
||||||
return acc;
|
return {};
|
||||||
}
|
}
|
||||||
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
|
}
|
||||||
return acc;
|
|
||||||
});
|
Future<Map<String, ElectrumTransactionInfo>> _fetchAddressHistory(
|
||||||
|
BitcoinAddressRecord addressRecord, Set<String> addressesSet, int currentHeight) async {
|
||||||
|
try {
|
||||||
|
final Map<String, ElectrumTransactionInfo> historiesWithDetails = {};
|
||||||
|
|
||||||
|
final history = await electrumClient
|
||||||
|
.getHistory(addressRecord.scriptHash ?? addressRecord.updateScriptHash(network));
|
||||||
|
|
||||||
|
if (history.isNotEmpty) {
|
||||||
|
addressRecord.setAsUsed();
|
||||||
|
|
||||||
|
await Future.wait(history.map((transaction) async {
|
||||||
|
final txid = transaction['tx_hash'] as String;
|
||||||
|
final height = transaction['height'] as int;
|
||||||
|
final storedTx = transactionHistory.transactions[txid];
|
||||||
|
|
||||||
|
if (storedTx != null) {
|
||||||
|
if (height > 0) {
|
||||||
|
storedTx.height = height;
|
||||||
|
// the tx's block itself is the first confirmation so add 1
|
||||||
|
storedTx.confirmations = currentHeight - height + 1;
|
||||||
|
storedTx.isPending = storedTx.confirmations == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
historiesWithDetails[txid] = storedTx;
|
||||||
|
} else {
|
||||||
|
final tx = await fetchTransactionInfo(
|
||||||
|
hash: txid, height: height, myAddresses: addressesSet, retryOnFailure: true);
|
||||||
|
|
||||||
|
if (tx != null) {
|
||||||
|
historiesWithDetails[txid] = tx;
|
||||||
|
|
||||||
|
// Got a new transaction fetched, add it to the transaction history
|
||||||
|
// instead of waiting all to finish, and next time it will be faster
|
||||||
|
transactionHistory.addOne(tx);
|
||||||
|
await transactionHistory.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.value(null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return historiesWithDetails;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
return {};
|
return {};
|
||||||
|
@ -654,10 +757,8 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
_isTransactionUpdating = true;
|
_isTransactionUpdating = true;
|
||||||
final transactions = await fetchTransactions();
|
await fetchTransactions();
|
||||||
transactionHistory.addMany(transactions);
|
|
||||||
walletAddresses.updateReceiveAddresses();
|
walletAddresses.updateReceiveAddresses();
|
||||||
await transactionHistory.save();
|
|
||||||
_isTransactionUpdating = false;
|
_isTransactionUpdating = false;
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
print(stacktrace);
|
print(stacktrace);
|
||||||
|
@ -688,11 +789,11 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ElectrumBalance> _fetchBalances() async {
|
Future<ElectrumBalance> _fetchBalances() async {
|
||||||
final addresses = walletAddresses.addresses.toList();
|
final addresses = walletAddresses.allAddresses.toList();
|
||||||
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
||||||
for (var i = 0; i < addresses.length; i++) {
|
for (var i = 0; i < addresses.length; i++) {
|
||||||
final addressRecord = addresses[i];
|
final addressRecord = addresses[i];
|
||||||
final sh = scriptHash(addressRecord.address, networkType: networkType);
|
final sh = scriptHash(addressRecord.address, network: network);
|
||||||
final balanceFuture = electrumClient.getBalance(sh);
|
final balanceFuture = electrumClient.getBalance(sh);
|
||||||
balanceFutures.add(balanceFuture);
|
balanceFutures.add(balanceFuture);
|
||||||
}
|
}
|
||||||
|
@ -701,6 +802,7 @@ abstract class ElectrumWalletBase
|
||||||
unspentCoinsInfo.values.forEach((info) {
|
unspentCoinsInfo.values.forEach((info) {
|
||||||
unspentCoins.forEach((element) {
|
unspentCoins.forEach((element) {
|
||||||
if (element.hash == info.hash &&
|
if (element.hash == info.hash &&
|
||||||
|
element.vout == info.vout &&
|
||||||
info.isFrozen &&
|
info.isFrozen &&
|
||||||
element.bitcoinAddressRecord.address == info.address &&
|
element.bitcoinAddressRecord.address == info.address &&
|
||||||
element.value == info.value) {
|
element.value == info.value) {
|
||||||
|
@ -738,10 +840,10 @@ abstract class ElectrumWalletBase
|
||||||
String getChangeAddress() {
|
String getChangeAddress() {
|
||||||
const minCountOfHiddenAddresses = 5;
|
const minCountOfHiddenAddresses = 5;
|
||||||
final random = Random();
|
final random = Random();
|
||||||
var addresses = walletAddresses.addresses.where((addr) => addr.isHidden).toList();
|
var addresses = walletAddresses.allAddresses.where((addr) => addr.isHidden).toList();
|
||||||
|
|
||||||
if (addresses.length < minCountOfHiddenAddresses) {
|
if (addresses.length < minCountOfHiddenAddresses) {
|
||||||
addresses = walletAddresses.addresses.toList();
|
addresses = walletAddresses.allAddresses.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return addresses[random.nextInt(addresses.length)].address;
|
return addresses[random.nextInt(addresses.length)].address;
|
||||||
|
@ -753,9 +855,62 @@ abstract class ElectrumWalletBase
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address = null}) {
|
String signMessage(String message, {String? address = null}) {
|
||||||
final index = address != null
|
final index = address != null
|
||||||
? walletAddresses.addresses.firstWhere((element) => element.address == address).index
|
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
|
||||||
: null;
|
: null;
|
||||||
final HD = index == null ? hd : hd.derive(index);
|
final HD = index == null ? hd : hd.derive(index);
|
||||||
return base64Encode(HD.signMessage(message));
|
return base64Encode(HD.signMessage(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EstimateTxParams {
|
||||||
|
EstimateTxParams(
|
||||||
|
{required this.amount,
|
||||||
|
required this.feeRate,
|
||||||
|
required this.priority,
|
||||||
|
required this.outputsCount,
|
||||||
|
required this.size});
|
||||||
|
|
||||||
|
final int amount;
|
||||||
|
final int feeRate;
|
||||||
|
final TransactionPriority priority;
|
||||||
|
final int outputsCount;
|
||||||
|
final int size;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EstimatedTxResult {
|
||||||
|
EstimatedTxResult(
|
||||||
|
{required this.utxos, required this.privateKeys, required this.fee, required this.amount});
|
||||||
|
|
||||||
|
final List<UtxoWithAddress> utxos;
|
||||||
|
final List<ECPrivate> privateKeys;
|
||||||
|
final int fee;
|
||||||
|
final int amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
||||||
|
if (P2pkhAddress.regex.hasMatch(address)) {
|
||||||
|
return P2pkhAddress.fromAddress(address: address, network: network);
|
||||||
|
} else if (P2shAddress.regex.hasMatch(address)) {
|
||||||
|
return P2shAddress.fromAddress(address: address, network: network);
|
||||||
|
} else if (P2wshAddress.regex.hasMatch(address)) {
|
||||||
|
return P2wshAddress.fromAddress(address: address, network: network);
|
||||||
|
} else if (P2trAddress.regex.hasMatch(address)) {
|
||||||
|
return P2trAddress.fromAddress(address: address, network: network);
|
||||||
|
} else {
|
||||||
|
return P2wpkhAddress.fromAddress(address: address, network: network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
|
||||||
|
if (type is P2pkhAddress) {
|
||||||
|
return P2pkhAddressType.p2pkh;
|
||||||
|
} else if (type is P2shAddress) {
|
||||||
|
return P2shAddressType.p2wpkhInP2sh;
|
||||||
|
} else if (type is P2wshAddress) {
|
||||||
|
return SegwitAddresType.p2wsh;
|
||||||
|
} else if (type is P2trAddress) {
|
||||||
|
return SegwitAddresType.p2tr;
|
||||||
|
} else {
|
||||||
|
return SegwitAddresType.p2wpkh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
|
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
import 'package:cw_bitcoin/electrum.dart';
|
||||||
import 'package:cw_bitcoin/script_hash.dart';
|
|
||||||
import 'package:cw_core/wallet_addresses.dart';
|
import 'package:cw_core/wallet_addresses.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
@ -12,25 +12,41 @@ part 'electrum_wallet_addresses.g.dart';
|
||||||
|
|
||||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
||||||
|
|
||||||
|
const List<BitcoinAddressType> ADDRESS_TYPES = [
|
||||||
|
SegwitAddresType.p2wpkh,
|
||||||
|
P2pkhAddressType.p2pkh,
|
||||||
|
SegwitAddresType.p2tr,
|
||||||
|
SegwitAddresType.p2wsh,
|
||||||
|
P2shAddressType.p2wpkhInP2sh,
|
||||||
|
];
|
||||||
|
|
||||||
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
ElectrumWalletAddressesBase(WalletInfo walletInfo,
|
ElectrumWalletAddressesBase(
|
||||||
{required this.mainHd,
|
WalletInfo walletInfo, {
|
||||||
required this.sideHd,
|
required this.mainHd,
|
||||||
required this.electrumClient,
|
required this.sideHd,
|
||||||
required this.networkType,
|
required this.electrumClient,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
required this.network,
|
||||||
int initialRegularAddressIndex = 0,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialChangeAddressIndex = 0})
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
: addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
|
}) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
|
||||||
|
addressesByReceiveType =
|
||||||
|
ObservableList<BitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
|
||||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||||
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
|
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
|
||||||
.toSet()),
|
.toSet()),
|
||||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
changeAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||||
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
|
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
|
||||||
.toSet()),
|
.toSet()),
|
||||||
currentReceiveAddressIndex = initialRegularAddressIndex,
|
currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {},
|
||||||
currentChangeAddressIndex = initialChangeAddressIndex,
|
currentChangeAddressIndexByType = initialChangeAddressIndex ?? {},
|
||||||
super(walletInfo);
|
_addressPageType = walletInfo.addressPageType != null
|
||||||
|
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
||||||
|
: SegwitAddresType.p2wpkh,
|
||||||
|
super(walletInfo) {
|
||||||
|
updateAddressesByMatch();
|
||||||
|
}
|
||||||
|
|
||||||
static const defaultReceiveAddressesCount = 22;
|
static const defaultReceiveAddressesCount = 22;
|
||||||
static const defaultChangeAddressesCount = 17;
|
static const defaultChangeAddressesCount = 17;
|
||||||
|
@ -40,37 +56,48 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address);
|
static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address);
|
||||||
|
|
||||||
final ObservableList<BitcoinAddressRecord> addresses;
|
final ObservableList<BitcoinAddressRecord> _addresses;
|
||||||
|
// Matched by addressPageType
|
||||||
|
late ObservableList<BitcoinAddressRecord> addressesByReceiveType;
|
||||||
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
||||||
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
||||||
final ElectrumClient electrumClient;
|
final ElectrumClient electrumClient;
|
||||||
final bitcoin.NetworkType networkType;
|
final BasedUtxoNetwork network;
|
||||||
final bitcoin.HDWallet mainHd;
|
final bitcoin.HDWallet mainHd;
|
||||||
final bitcoin.HDWallet sideHd;
|
final bitcoin.HDWallet sideHd;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
BitcoinAddressType _addressPageType = SegwitAddresType.p2wpkh;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
BitcoinAddressType get addressPageType => _addressPageType;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
List<BitcoinAddressRecord> get allAddresses => _addresses;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@computed
|
@computed
|
||||||
String get address {
|
String get address {
|
||||||
if (isEnabledAutoGenerateSubaddress) {
|
String receiveAddress;
|
||||||
if (receiveAddresses.isEmpty) {
|
|
||||||
final newAddress = generateNewAddress(hd: mainHd).address;
|
|
||||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
|
|
||||||
}
|
|
||||||
final receiveAddress = receiveAddresses.first.address;
|
|
||||||
|
|
||||||
return walletInfo.type == WalletType.bitcoinCash
|
final typeMatchingReceiveAddresses = receiveAddresses.where(_isAddressPageTypeMatch);
|
||||||
? toCashAddr(receiveAddress)
|
|
||||||
: receiveAddress;
|
if ((isEnabledAutoGenerateSubaddress && receiveAddresses.isEmpty) ||
|
||||||
|
typeMatchingReceiveAddresses.isEmpty) {
|
||||||
|
receiveAddress = generateNewAddress().address;
|
||||||
} else {
|
} else {
|
||||||
final receiveAddress = (receiveAddresses.first.address != addresses.first.address &&
|
final previousAddressMatchesType =
|
||||||
previousAddressRecord != null)
|
previousAddressRecord != null && previousAddressRecord!.type == addressPageType;
|
||||||
? previousAddressRecord!.address
|
|
||||||
: addresses.first.address;
|
|
||||||
|
|
||||||
return walletInfo.type == WalletType.bitcoinCash
|
if (previousAddressMatchesType &&
|
||||||
? toCashAddr(receiveAddress)
|
typeMatchingReceiveAddresses.first.address != addressesByReceiveType.first.address) {
|
||||||
: receiveAddress;
|
receiveAddress = previousAddressRecord!.address;
|
||||||
|
} else {
|
||||||
|
receiveAddress = typeMatchingReceiveAddresses.first.address;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
|
@ -81,7 +108,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
if (addr.startsWith('bitcoincash:')) {
|
if (addr.startsWith('bitcoincash:')) {
|
||||||
addr = toLegacy(addr);
|
addr = toLegacy(addr);
|
||||||
}
|
}
|
||||||
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == addr);
|
final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == addr);
|
||||||
|
|
||||||
previousAddressRecord = addressRecord;
|
previousAddressRecord = addressRecord;
|
||||||
receiveAddresses.remove(addressRecord);
|
receiveAddresses.remove(addressRecord);
|
||||||
|
@ -89,16 +116,29 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get primaryAddress => getAddress(index: 0, hd: mainHd);
|
String get primaryAddress => getAddress(index: 0, hd: mainHd, addressType: addressPageType);
|
||||||
|
|
||||||
int currentReceiveAddressIndex;
|
Map<String, int> currentReceiveAddressIndexByType;
|
||||||
int currentChangeAddressIndex;
|
|
||||||
|
int get currentReceiveAddressIndex =>
|
||||||
|
currentReceiveAddressIndexByType[_addressPageType.toString()] ?? 0;
|
||||||
|
|
||||||
|
void set currentReceiveAddressIndex(int index) =>
|
||||||
|
currentReceiveAddressIndexByType[_addressPageType.toString()] = index;
|
||||||
|
|
||||||
|
Map<String, int> currentChangeAddressIndexByType;
|
||||||
|
|
||||||
|
int get currentChangeAddressIndex =>
|
||||||
|
currentChangeAddressIndexByType[_addressPageType.toString()] ?? 0;
|
||||||
|
|
||||||
|
void set currentChangeAddressIndex(int index) =>
|
||||||
|
currentChangeAddressIndexByType[_addressPageType.toString()] = index;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
BitcoinAddressRecord? previousAddressRecord;
|
BitcoinAddressRecord? previousAddressRecord;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
int get totalCountOfReceiveAddresses => addresses.fold(0, (acc, addressRecord) {
|
int get totalCountOfReceiveAddresses => addressesByReceiveType.fold(0, (acc, addressRecord) {
|
||||||
if (!addressRecord.isHidden) {
|
if (!addressRecord.isHidden) {
|
||||||
return acc + 1;
|
return acc + 1;
|
||||||
}
|
}
|
||||||
|
@ -106,22 +146,21 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
});
|
});
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
int get totalCountOfChangeAddresses => addresses.fold(0, (acc, addressRecord) {
|
int get totalCountOfChangeAddresses => addressesByReceiveType.fold(0, (acc, addressRecord) {
|
||||||
if (addressRecord.isHidden) {
|
if (addressRecord.isHidden) {
|
||||||
return acc + 1;
|
return acc + 1;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> discoverAddresses() async {
|
|
||||||
await _discoverAddresses(mainHd, false);
|
|
||||||
await _discoverAddresses(sideHd, true);
|
|
||||||
await updateAddressesInBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
await _generateInitialAddresses();
|
await _generateInitialAddresses();
|
||||||
|
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||||
|
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||||
|
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||||
|
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||||
|
updateAddressesByMatch();
|
||||||
updateReceiveAddresses();
|
updateReceiveAddresses();
|
||||||
updateChangeAddresses();
|
updateChangeAddresses();
|
||||||
await updateAddressesInBox();
|
await updateAddressesInBox();
|
||||||
|
@ -141,10 +180,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
if (changeAddresses.isEmpty) {
|
if (changeAddresses.isEmpty) {
|
||||||
final newAddresses = await _createNewAddresses(gap,
|
final newAddresses = await _createNewAddresses(gap,
|
||||||
hd: sideHd,
|
|
||||||
startIndex: totalCountOfChangeAddresses > 0 ? totalCountOfChangeAddresses - 1 : 0,
|
startIndex: totalCountOfChangeAddresses > 0 ? totalCountOfChangeAddresses - 1 : 0,
|
||||||
isHidden: true);
|
isHidden: true);
|
||||||
_addAddresses(newAddresses);
|
addAddresses(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentChangeAddressIndex >= changeAddresses.length) {
|
if (currentChangeAddressIndex >= changeAddresses.length) {
|
||||||
|
@ -157,19 +195,26 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
BitcoinAddressRecord generateNewAddress({bitcoin.HDWallet? hd, String? label}) {
|
BitcoinAddressRecord generateNewAddress({String label = ''}) {
|
||||||
final isHidden = hd == sideHd;
|
final newAddressIndex = addressesByReceiveType.fold(
|
||||||
|
0, (int acc, addressRecord) => addressRecord.isHidden == false ? acc + 1 : acc);
|
||||||
|
|
||||||
final newAddressIndex = addresses.fold(
|
final address = BitcoinAddressRecord(
|
||||||
0, (int acc, addressRecord) => isHidden == addressRecord.isHidden ? acc + 1 : acc);
|
getAddress(index: newAddressIndex, hd: mainHd, addressType: addressPageType),
|
||||||
|
index: newAddressIndex,
|
||||||
final address = BitcoinAddressRecord(getAddress(index: newAddressIndex, hd: hd ?? sideHd),
|
isHidden: false,
|
||||||
index: newAddressIndex, isHidden: isHidden, name: label ?? '');
|
name: label,
|
||||||
addresses.add(address);
|
type: addressPageType,
|
||||||
|
network: network,
|
||||||
|
);
|
||||||
|
_addresses.add(address);
|
||||||
|
updateAddressesByMatch();
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getAddress({required int index, required bitcoin.HDWallet hd}) => '';
|
String getAddress(
|
||||||
|
{required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
|
||||||
|
'';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateAddressesInBox() async {
|
Future<void> updateAddressesInBox() async {
|
||||||
|
@ -187,126 +232,138 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
if (address.startsWith('bitcoincash:')) {
|
if (address.startsWith('bitcoincash:')) {
|
||||||
address = toLegacy(address);
|
address = toLegacy(address);
|
||||||
}
|
}
|
||||||
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == address);
|
final addressRecord =
|
||||||
|
_addresses.firstWhere((addressRecord) => addressRecord.address == address);
|
||||||
addressRecord.setNewName(label);
|
addressRecord.setNewName(label);
|
||||||
final index = addresses.indexOf(addressRecord);
|
final index = _addresses.indexOf(addressRecord);
|
||||||
addresses.remove(addressRecord);
|
_addresses.remove(addressRecord);
|
||||||
addresses.insert(index, addressRecord);
|
_addresses.insert(index, addressRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void updateAddressesByMatch() {
|
||||||
|
addressesByReceiveType.clear();
|
||||||
|
addressesByReceiveType.addAll(_addresses.where(_isAddressPageTypeMatch).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void updateReceiveAddresses() {
|
void updateReceiveAddresses() {
|
||||||
receiveAddresses.removeRange(0, receiveAddresses.length);
|
receiveAddresses.removeRange(0, receiveAddresses.length);
|
||||||
final newAddresses =
|
final newAddresses =
|
||||||
addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
_addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||||
receiveAddresses.addAll(newAddresses);
|
receiveAddresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void updateChangeAddresses() {
|
void updateChangeAddresses() {
|
||||||
changeAddresses.removeRange(0, changeAddresses.length);
|
changeAddresses.removeRange(0, changeAddresses.length);
|
||||||
final newAddresses =
|
final newAddresses = _addresses.where((addressRecord) =>
|
||||||
addresses.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
|
addressRecord.isHidden &&
|
||||||
|
!addressRecord.isUsed &&
|
||||||
|
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
|
||||||
|
addressRecord.type == SegwitAddresType.p2wpkh);
|
||||||
changeAddresses.addAll(newAddresses);
|
changeAddresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
|
@action
|
||||||
var hasAddrUse = true;
|
Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
|
||||||
List<BitcoinAddressRecord> addrs;
|
Future<String?> Function(BitcoinAddressRecord, Set<String>) getAddressHistory,
|
||||||
|
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||||
if (addresses.isNotEmpty) {
|
if (!isHidden) {
|
||||||
|
_validateSideHdAddresses(addressList.toList());
|
||||||
|
|
||||||
if(!isHidden) {
|
|
||||||
final receiveAddressesList = addresses.where((addr) => !addr.isHidden).toList();
|
|
||||||
validateSideHdAddresses(receiveAddressesList);
|
|
||||||
}
|
|
||||||
|
|
||||||
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
|
||||||
} else {
|
|
||||||
addrs = await _createNewAddresses(
|
|
||||||
isHidden ? defaultChangeAddressesCount : defaultReceiveAddressesCount,
|
|
||||||
startIndex: 0,
|
|
||||||
hd: hd,
|
|
||||||
isHidden: isHidden);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (hasAddrUse) {
|
final newAddresses = await _createNewAddresses(gap,
|
||||||
final addr = addrs.last.address;
|
startIndex: addressList.length, isHidden: isHidden, type: type);
|
||||||
hasAddrUse = await _hasAddressUsed(addr);
|
addAddresses(newAddresses);
|
||||||
|
|
||||||
if (!hasAddrUse) {
|
final addressesWithHistory = await Future.wait(newAddresses
|
||||||
break;
|
.map((addr) => getAddressHistory(addr, _addresses.map((e) => e.address).toSet())));
|
||||||
}
|
final isLastAddressUsed = addressesWithHistory.last == addressList.last.address;
|
||||||
|
|
||||||
final start = addrs.length;
|
if (isLastAddressUsed) {
|
||||||
final count = start + gap;
|
discoverAddresses(addressList, isHidden, getAddressHistory, type: type);
|
||||||
final batch = await _createNewAddresses(count, startIndex: start, hd: hd, isHidden: isHidden);
|
|
||||||
addrs.addAll(batch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addresses.length < addrs.length) {
|
|
||||||
_addAddresses(addrs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _generateInitialAddresses() async {
|
Future<void> _generateInitialAddresses(
|
||||||
|
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||||
var countOfReceiveAddresses = 0;
|
var countOfReceiveAddresses = 0;
|
||||||
var countOfHiddenAddresses = 0;
|
var countOfHiddenAddresses = 0;
|
||||||
|
|
||||||
addresses.forEach((addr) {
|
_addresses.forEach((addr) {
|
||||||
if (addr.isHidden) {
|
if (addr.type == type) {
|
||||||
countOfHiddenAddresses += 1;
|
if (addr.isHidden) {
|
||||||
return;
|
countOfHiddenAddresses += 1;
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
countOfReceiveAddresses += 1;
|
countOfReceiveAddresses += 1;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||||
final newAddresses = await _createNewAddresses(addressesCount,
|
final newAddresses = await _createNewAddresses(addressesCount,
|
||||||
startIndex: countOfReceiveAddresses, hd: mainHd, isHidden: false);
|
startIndex: countOfReceiveAddresses, isHidden: false, type: type);
|
||||||
addresses.addAll(newAddresses);
|
addAddresses(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||||
final newAddresses = await _createNewAddresses(addressesCount,
|
final newAddresses = await _createNewAddresses(addressesCount,
|
||||||
startIndex: countOfHiddenAddresses, hd: sideHd, isHidden: true);
|
startIndex: countOfHiddenAddresses, isHidden: true, type: type);
|
||||||
addresses.addAll(newAddresses);
|
addAddresses(newAddresses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||||
{required bitcoin.HDWallet hd, int startIndex = 0, bool isHidden = false}) async {
|
{int startIndex = 0, bool isHidden = false, BitcoinAddressType? type}) async {
|
||||||
final list = <BitcoinAddressRecord>[];
|
final list = <BitcoinAddressRecord>[];
|
||||||
|
|
||||||
for (var i = startIndex; i < count + startIndex; i++) {
|
for (var i = startIndex; i < count + startIndex; i++) {
|
||||||
final address =
|
final address = BitcoinAddressRecord(
|
||||||
BitcoinAddressRecord(getAddress(index: i, hd: hd), index: i, isHidden: isHidden);
|
getAddress(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType),
|
||||||
|
index: i,
|
||||||
|
isHidden: isHidden,
|
||||||
|
type: type ?? addressPageType,
|
||||||
|
network: network,
|
||||||
|
);
|
||||||
list.add(address);
|
list.add(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addAddresses(Iterable<BitcoinAddressRecord> addresses) {
|
@action
|
||||||
final addressesSet = this.addresses.toSet();
|
void addAddresses(Iterable<BitcoinAddressRecord> addresses) {
|
||||||
|
final addressesSet = this._addresses.toSet();
|
||||||
addressesSet.addAll(addresses);
|
addressesSet.addAll(addresses);
|
||||||
this.addresses.removeRange(0, this.addresses.length);
|
this._addresses.clear();
|
||||||
this.addresses.addAll(addressesSet);
|
this._addresses.addAll(addressesSet);
|
||||||
|
updateAddressesByMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _hasAddressUsed(String address) async {
|
void _validateSideHdAddresses(List<BitcoinAddressRecord> addrWithTransactions) {
|
||||||
final sh = scriptHash(address, networkType: networkType);
|
|
||||||
final transactionHistory = await electrumClient.getHistory(sh);
|
|
||||||
return transactionHistory.isNotEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
void validateSideHdAddresses(List<BitcoinAddressRecord> addrWithTransactions) {
|
|
||||||
addrWithTransactions.forEach((element) {
|
addrWithTransactions.forEach((element) {
|
||||||
if (element.address != getAddress(index: element.index, hd: mainHd)) element.isHidden = true;
|
if (element.address !=
|
||||||
|
getAddress(index: element.index, hd: mainHd, addressType: element.type))
|
||||||
|
element.isHidden = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
Future<void> setAddressType(BitcoinAddressType type) async {
|
||||||
|
_addressPageType = type;
|
||||||
|
updateAddressesByMatch();
|
||||||
|
walletInfo.addressPageType = addressPageType.toString();
|
||||||
|
await walletInfo.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isAddressPageTypeMatch(BitcoinAddressRecord addressRecord) {
|
||||||
|
return _isAddressByType(addressRecord, addressPageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
bitcoin.HDWallet _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
|
||||||
|
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||||
import 'package:cw_core/pathForWallet.dart';
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
import 'package:cw_core/utils/file.dart';
|
import 'package:cw_core/utils/file.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
class ElectrumWallletSnapshot {
|
class ElectrumWalletSnapshot {
|
||||||
ElectrumWallletSnapshot({
|
ElectrumWalletSnapshot({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.password,
|
required this.password,
|
||||||
|
@ -14,19 +15,24 @@ class ElectrumWallletSnapshot {
|
||||||
required this.addresses,
|
required this.addresses,
|
||||||
required this.balance,
|
required this.balance,
|
||||||
required this.regularAddressIndex,
|
required this.regularAddressIndex,
|
||||||
required this.changeAddressIndex});
|
required this.changeAddressIndex,
|
||||||
|
required this.addressPageType,
|
||||||
|
required this.network,
|
||||||
|
});
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final String password;
|
final String password;
|
||||||
final WalletType type;
|
final WalletType type;
|
||||||
|
final String addressPageType;
|
||||||
|
final BasedUtxoNetwork network;
|
||||||
|
|
||||||
String mnemonic;
|
String mnemonic;
|
||||||
List<BitcoinAddressRecord> addresses;
|
List<BitcoinAddressRecord> addresses;
|
||||||
ElectrumBalance balance;
|
ElectrumBalance balance;
|
||||||
int regularAddressIndex;
|
Map<String, int> regularAddressIndex;
|
||||||
int changeAddressIndex;
|
Map<String, int> changeAddressIndex;
|
||||||
|
|
||||||
static Future<ElectrumWallletSnapshot> load(String name, WalletType type, String password) async {
|
static Future<ElectrumWalletSnapshot> load(String name, WalletType type, String password, BasedUtxoNetwork? network) async {
|
||||||
final path = await pathForWallet(name: name, type: type);
|
final path = await pathForWallet(name: name, type: type);
|
||||||
final jsonSource = await read(path: path, password: password);
|
final jsonSource = await read(path: path, password: password);
|
||||||
final data = json.decode(jsonSource) as Map;
|
final data = json.decode(jsonSource) as Map;
|
||||||
|
@ -34,26 +40,39 @@ class ElectrumWallletSnapshot {
|
||||||
final mnemonic = data['mnemonic'] as String;
|
final mnemonic = data['mnemonic'] as String;
|
||||||
final addresses = addressesTmp
|
final addresses = addressesTmp
|
||||||
.whereType<String>()
|
.whereType<String>()
|
||||||
.map((addr) => BitcoinAddressRecord.fromJSON(addr))
|
.map((addr) => BitcoinAddressRecord.fromJSON(addr, network))
|
||||||
.toList();
|
.toList();
|
||||||
final balance = ElectrumBalance.fromJSON(data['balance'] as String) ??
|
final balance = ElectrumBalance.fromJSON(data['balance'] as String) ??
|
||||||
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
||||||
var regularAddressIndex = 0;
|
var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||||
var changeAddressIndex = 0;
|
var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
regularAddressIndex = int.parse(data['account_index'] as String? ?? '0');
|
regularAddressIndexByType = {
|
||||||
changeAddressIndex = int.parse(data['change_address_index'] as String? ?? '0');
|
SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
|
||||||
} catch (_) {}
|
};
|
||||||
|
changeAddressIndexByType = {
|
||||||
|
SegwitAddresType.p2wpkh.toString():
|
||||||
|
int.parse(data['change_address_index'] as String? ?? '0')
|
||||||
|
};
|
||||||
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
regularAddressIndexByType = data["account_index"] as Map<String, int>? ?? {};
|
||||||
|
changeAddressIndexByType = data["change_address_index"] as Map<String, int>? ?? {};
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
return ElectrumWallletSnapshot(
|
return ElectrumWalletSnapshot(
|
||||||
name: name,
|
name: name,
|
||||||
type: type,
|
type: type,
|
||||||
password: password,
|
password: password,
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
addresses: addresses,
|
addresses: addresses,
|
||||||
balance: balance,
|
balance: balance,
|
||||||
regularAddressIndex: regularAddressIndex,
|
regularAddressIndex: regularAddressIndexByType,
|
||||||
changeAddressIndex: changeAddressIndex);
|
changeAddressIndex: changeAddressIndexByType,
|
||||||
|
addressPageType: data['address_page_type'] as String? ?? SegwitAddresType.p2wpkh.toString(),
|
||||||
|
network: data['network_type'] == 'testnet' ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
@ -20,17 +21,18 @@ part 'litecoin_wallet.g.dart';
|
||||||
class LitecoinWallet = LitecoinWalletBase with _$LitecoinWallet;
|
class LitecoinWallet = LitecoinWalletBase with _$LitecoinWallet;
|
||||||
|
|
||||||
abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
LitecoinWalletBase(
|
LitecoinWalletBase({
|
||||||
{required String mnemonic,
|
required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required Uint8List seedBytes,
|
required Uint8List seedBytes,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
String? addressPageType,
|
||||||
ElectrumBalance? initialBalance,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
ElectrumBalance? initialBalance,
|
||||||
int initialChangeAddressIndex = 0})
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
: super(
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
|
}) : super(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -41,41 +43,42 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
currency: CryptoCurrency.ltc) {
|
currency: CryptoCurrency.ltc) {
|
||||||
walletAddresses = LitecoinWalletAddresses(
|
walletAddresses = LitecoinWalletAddresses(
|
||||||
walletInfo,
|
walletInfo,
|
||||||
electrumClient: electrumClient,
|
electrumClient: electrumClient,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: bitcoin.HDWallet
|
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
||||||
.fromSeed(seedBytes, network: networkType)
|
network: network,
|
||||||
.derivePath("m/0'/1"),
|
);
|
||||||
networkType: networkType,);
|
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LitecoinWallet> create({
|
static Future<LitecoinWallet> create(
|
||||||
required String mnemonic,
|
{required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
String? addressPageType,
|
||||||
ElectrumBalance? initialBalance,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
ElectrumBalance? initialBalance,
|
||||||
int initialChangeAddressIndex = 0
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
}) async {
|
Map<String, int>? initialChangeAddressIndex}) async {
|
||||||
return LitecoinWallet(
|
return LitecoinWallet(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
seedBytes: await mnemonicToSeedBytes(mnemonic),
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex);
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
|
addressPageType: addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LitecoinWallet> open({
|
static Future<LitecoinWallet> open({
|
||||||
|
@ -84,17 +87,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
final snp = await ElectrumWallletSnapshot.load (name, walletInfo.type, password);
|
final snp =
|
||||||
|
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
|
||||||
return LitecoinWallet(
|
return LitecoinWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: snp.addresses,
|
initialAddresses: snp.addresses,
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex);
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
|
addressPageType: snp.addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,39 +1,28 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
|
||||||
import 'package:cw_bitcoin/utils.dart';
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
part 'litecoin_wallet_addresses.g.dart';
|
part 'litecoin_wallet_addresses.g.dart';
|
||||||
|
|
||||||
class LitecoinWalletAddresses = LitecoinWalletAddressesBase
|
class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses;
|
||||||
with _$LitecoinWalletAddresses;
|
|
||||||
|
|
||||||
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
|
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
||||||
with Store {
|
|
||||||
LitecoinWalletAddressesBase(
|
LitecoinWalletAddressesBase(
|
||||||
WalletInfo walletInfo,
|
WalletInfo walletInfo, {
|
||||||
{required bitcoin.HDWallet mainHd,
|
required super.mainHd,
|
||||||
required bitcoin.HDWallet sideHd,
|
required super.sideHd,
|
||||||
required bitcoin.NetworkType networkType,
|
required super.network,
|
||||||
required ElectrumClient electrumClient,
|
required super.electrumClient,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
super.initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
super.initialRegularAddressIndex,
|
||||||
int initialChangeAddressIndex = 0})
|
super.initialChangeAddressIndex,
|
||||||
: super(
|
}) : super(walletInfo);
|
||||||
walletInfo,
|
|
||||||
initialAddresses: initialAddresses,
|
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
|
||||||
mainHd: mainHd,
|
|
||||||
sideHd: sideHd,
|
|
||||||
electrumClient: electrumClient,
|
|
||||||
networkType: networkType);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
String getAddress(
|
||||||
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
{required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
|
||||||
}
|
generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class LitecoinWalletService extends WalletService<
|
||||||
WalletType getType() => WalletType.litecoin;
|
WalletType getType() => WalletType.litecoin;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
final wallet = await LitecoinWalletBase.create(
|
final wallet = await LitecoinWalletBase.create(
|
||||||
mnemonic: await generateMnemonic(),
|
mnemonic: await generateMnemonic(),
|
||||||
password: credentials.password!,
|
password: credentials.password!,
|
||||||
|
@ -94,12 +94,12 @@ class LitecoinWalletService extends WalletService<
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LitecoinWallet> restoreFromKeys(
|
Future<LitecoinWallet> restoreFromKeys(
|
||||||
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async =>
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LitecoinWallet> restoreFromSeed(
|
Future<LitecoinWallet> restoreFromSeed(
|
||||||
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||||
if (!validateMnemonic(credentials.mnemonic)) {
|
if (!validateMnemonic(credentials.mnemonic)) {
|
||||||
throw BitcoinMnemonicIsIncorrectException();
|
throw BitcoinMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:cw_bitcoin/bitcoin_commit_transaction_exception.dart';
|
import 'package:cw_bitcoin/bitcoin_commit_transaction_exception.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_core/pending_transaction.dart';
|
import 'package:cw_core/pending_transaction.dart';
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
import 'package:cw_bitcoin/electrum.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
|
@ -9,22 +9,21 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
class PendingBitcoinTransaction with PendingTransaction {
|
class PendingBitcoinTransaction with PendingTransaction {
|
||||||
PendingBitcoinTransaction(this._tx, this.type,
|
PendingBitcoinTransaction(this._tx, this.type,
|
||||||
{required this.electrumClient,
|
{required this.electrumClient, required this.amount, required this.fee, this.network})
|
||||||
required this.amount,
|
|
||||||
required this.fee})
|
|
||||||
: _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
|
: _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
|
||||||
|
|
||||||
final WalletType type;
|
final WalletType type;
|
||||||
final bitcoin.Transaction _tx;
|
final BtcTransaction _tx;
|
||||||
final ElectrumClient electrumClient;
|
final ElectrumClient electrumClient;
|
||||||
final int amount;
|
final int amount;
|
||||||
final int fee;
|
final int fee;
|
||||||
|
final BasedUtxoNetwork? network;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id => _tx.getId();
|
String get id => _tx.txId();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get hex => _tx.toHex();
|
String get hex => _tx.serialize();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||||
|
@ -36,18 +35,16 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> commit() async {
|
Future<void> commit() async {
|
||||||
final result =
|
final result = await electrumClient.broadcastTransaction(transactionRaw: hex, network: network);
|
||||||
await electrumClient.broadcastTransaction(transactionRaw: _tx.toHex());
|
|
||||||
|
|
||||||
if (result.isEmpty) {
|
if (result.isEmpty) {
|
||||||
throw BitcoinCommitTransactionException();
|
throw BitcoinCommitTransactionException();
|
||||||
}
|
}
|
||||||
|
|
||||||
_listeners?.forEach((listener) => listener(transactionInfo()));
|
_listeners.forEach((listener) => listener(transactionInfo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void addListener(
|
void addListener(void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||||
void Function(ElectrumTransactionInfo transaction) listener) =>
|
|
||||||
_listeners.add(listener);
|
_listeners.add(listener);
|
||||||
|
|
||||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
|
|
||||||
String scriptHash(String address, {required bitcoin.NetworkType networkType}) {
|
String scriptHash(String address, {required BasedUtxoNetwork network}) {
|
||||||
final outputScript =
|
final outputScript = addressToOutputScript(address: address, network: network);
|
||||||
bitcoin.Address.addressToOutputScript(address, networkType);
|
|
||||||
final parts = sha256.convert(outputScript).toString().split('');
|
final parts = sha256.convert(outputScript).toString().split('');
|
||||||
var res = '';
|
var res = '';
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,33 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||||
import 'package:hex/hex.dart';
|
import 'package:hex/hex.dart';
|
||||||
|
|
||||||
bitcoin.PaymentData generatePaymentData(
|
bitcoin.PaymentData generatePaymentData({required bitcoin.HDWallet hd, required int index}) =>
|
||||||
{required bitcoin.HDWallet hd, required int index}) =>
|
PaymentData(pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!)));
|
||||||
PaymentData(
|
|
||||||
pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!)));
|
|
||||||
|
|
||||||
bitcoin.ECPair generateKeyPair(
|
ECPrivate generateECPrivate(
|
||||||
{required bitcoin.HDWallet hd,
|
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||||
required int index,
|
ECPrivate.fromWif(hd.derive(index).wif!, netVersion: network.wifNetVer);
|
||||||
required bitcoin.NetworkType network}) =>
|
|
||||||
bitcoin.ECPair.fromWIF(hd.derive(index).wif!, network: network);
|
|
||||||
|
|
||||||
String generateP2WPKHAddress(
|
String generateP2WPKHAddress(
|
||||||
{required bitcoin.HDWallet hd,
|
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||||
required int index,
|
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhAddress().toAddress(network);
|
||||||
required bitcoin.NetworkType networkType}) =>
|
|
||||||
bitcoin
|
|
||||||
.P2WPKH(
|
|
||||||
data: PaymentData(
|
|
||||||
pubkey:
|
|
||||||
Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!))),
|
|
||||||
network: networkType)
|
|
||||||
.data
|
|
||||||
.address!;
|
|
||||||
|
|
||||||
String generateP2WPKHAddressByPath(
|
String generateP2SHAddress(
|
||||||
{required bitcoin.HDWallet hd,
|
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||||
required String path,
|
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wpkhInP2sh().toAddress(network);
|
||||||
required bitcoin.NetworkType networkType}) =>
|
|
||||||
bitcoin
|
String generateP2WSHAddress(
|
||||||
.P2WPKH(
|
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||||
data: PaymentData(
|
ECPublic.fromHex(hd.derive(index).pubKey!).toP2wshAddress().toAddress(network);
|
||||||
pubkey:
|
|
||||||
Uint8List.fromList(HEX.decode(hd.derivePath(path).pubKey!))),
|
|
||||||
network: networkType)
|
|
||||||
.data
|
|
||||||
.address!;
|
|
||||||
|
|
||||||
String generateP2PKHAddress(
|
String generateP2PKHAddress(
|
||||||
{required bitcoin.HDWallet hd,
|
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||||
required int index,
|
ECPublic.fromHex(hd.derive(index).pubKey!).toP2pkhAddress().toAddress(network);
|
||||||
required bitcoin.NetworkType networkType}) =>
|
|
||||||
bitcoin
|
String generateP2TRAddress(
|
||||||
.P2PKH(
|
{required bitcoin.HDWallet hd, required int index, required BasedUtxoNetwork network}) =>
|
||||||
data: PaymentData(
|
ECPublic.fromHex(hd.derive(index).pubKey!).toTaprootAddress().toAddress(network);
|
||||||
pubkey:
|
|
||||||
Uint8List.fromList(HEX.decode(hd.derive(index).pubKey!))),
|
|
||||||
network: networkType)
|
|
||||||
.data
|
|
||||||
.address!;
|
|
||||||
|
|
|
@ -21,18 +21,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
|
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.4.2"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039
|
sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.5.2"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -75,6 +75,15 @@ packages:
|
||||||
url: "https://github.com/cake-tech/bitbox-flutter.git"
|
url: "https://github.com/cake-tech/bitbox-flutter.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
|
bitcoin_base:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: cake-update-v1
|
||||||
|
resolved-ref: "9611e9db77e92a8434e918cdfb620068f6fcb1aa"
|
||||||
|
url: "https://github.com/cake-tech/bitcoin_base.git"
|
||||||
|
source: git
|
||||||
|
version: "4.0.0"
|
||||||
bitcoin_flutter:
|
bitcoin_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -84,6 +93,14 @@ packages:
|
||||||
url: "https://github.com/cake-tech/bitcoin_flutter.git"
|
url: "https://github.com/cake-tech/bitcoin_flutter.git"
|
||||||
source: git
|
source: git
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
blockchain_utils:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: blockchain_utils
|
||||||
|
sha256: "9701dfaa74caad4daae1785f1ec4445cf7fb94e45620bc3a4aca1b9b281dc6c9"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -104,10 +121,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.4.1"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -120,10 +137,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "4.0.1"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -136,18 +153,18 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.3"
|
version: "2.4.8"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
|
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.7"
|
version: "7.2.10"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -160,10 +177,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
|
sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.3"
|
version: "8.9.0"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -176,10 +193,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: checked_yaml
|
name: checked_yaml
|
||||||
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
|
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.3"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -192,10 +209,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: code_builder
|
name: code_builder
|
||||||
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
|
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "4.10.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -216,18 +233,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
cryptography:
|
cryptography:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cryptography
|
name: cryptography
|
||||||
sha256: e0e37f79665cd5c86e8897f9abe1accfe813c0cc5299dab22256e22fddc1fef8
|
sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.5.0"
|
||||||
cw_core:
|
cw_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -247,10 +264,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: encrypt
|
name: encrypt
|
||||||
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
|
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.1"
|
version: "5.0.3"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -263,10 +280,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.1.0"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -292,10 +309,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_mobx
|
name: flutter_mobx
|
||||||
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
|
sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6+5"
|
version: "2.2.0+2"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -313,18 +330,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: glob
|
name: glob
|
||||||
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
|
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: graphs
|
name: graphs
|
||||||
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
|
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.3.1"
|
||||||
hex:
|
hex:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -401,18 +418,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.8.1"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: logging
|
name: logging
|
||||||
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
|
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.2.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -449,18 +466,26 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobx
|
name: mobx
|
||||||
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
|
sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3+1"
|
version: "2.3.0+1"
|
||||||
mobx_codegen:
|
mobx_codegen:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181"
|
sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
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:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -481,26 +506,26 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
|
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.2"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -513,10 +538,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -529,26 +554,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.4"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.8"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
|
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.6.2"
|
version: "3.7.4"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -557,30 +582,30 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.1"
|
||||||
process:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: process
|
name: provider
|
||||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "6.1.1"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
|
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
|
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.3"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -593,18 +618,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
|
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
|
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.4"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -702,10 +727,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.2"
|
||||||
unorm_dart:
|
unorm_dart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -726,42 +751,42 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.1.0"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
|
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.4.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "5.0.9"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
|
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+3"
|
version: "1.0.4"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
|
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.0.0 <4.0.0"
|
dart: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=3.7.0"
|
flutter: ">=3.10.0"
|
||||||
|
|
|
@ -30,6 +30,11 @@ dependencies:
|
||||||
rxdart: ^0.27.5
|
rxdart: ^0.27.5
|
||||||
unorm_dart: ^0.2.0
|
unorm_dart: ^0.2.0
|
||||||
cryptography: ^2.0.5
|
cryptography: ^2.0.5
|
||||||
|
bitcoin_base:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/bitcoin_base.git
|
||||||
|
ref: cake-update-v1
|
||||||
|
blockchain_utils: ^1.6.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -28,17 +28,18 @@ part 'bitcoin_cash_wallet.g.dart';
|
||||||
class BitcoinCashWallet = BitcoinCashWalletBase with _$BitcoinCashWallet;
|
class BitcoinCashWallet = BitcoinCashWalletBase with _$BitcoinCashWallet;
|
||||||
|
|
||||||
abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
BitcoinCashWalletBase(
|
BitcoinCashWalletBase({
|
||||||
{required String mnemonic,
|
required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required Uint8List seedBytes,
|
required Uint8List seedBytes,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
String? addressPageType,
|
||||||
ElectrumBalance? initialBalance,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
int initialRegularAddressIndex = 0,
|
ElectrumBalance? initialBalance,
|
||||||
int initialChangeAddressIndex = 0})
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
: super(
|
Map<String, int>? initialChangeAddressIndex,
|
||||||
|
}) : super(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -48,40 +49,43 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
currency: CryptoCurrency.bch) {
|
currency: CryptoCurrency.bch) {
|
||||||
walletAddresses = BitcoinCashWalletAddresses(walletInfo,
|
walletAddresses = BitcoinCashWalletAddresses(
|
||||||
electrumClient: electrumClient,
|
walletInfo,
|
||||||
initialAddresses: initialAddresses,
|
electrumClient: electrumClient,
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialAddresses: initialAddresses,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
mainHd: hd,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes)
|
mainHd: hd,
|
||||||
.derivePath("m/44'/145'/0'/1"),
|
sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"),
|
||||||
networkType: networkType);
|
network: network,
|
||||||
|
);
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Future<BitcoinCashWallet> create(
|
static Future<BitcoinCashWallet> create(
|
||||||
{required String mnemonic,
|
{required String mnemonic,
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
|
String? addressPageType,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
ElectrumBalance? initialBalance,
|
ElectrumBalance? initialBalance,
|
||||||
int initialRegularAddressIndex = 0,
|
Map<String, int>? initialRegularAddressIndex,
|
||||||
int initialChangeAddressIndex = 0}) async {
|
Map<String, int>? initialChangeAddressIndex}) async {
|
||||||
return BitcoinCashWallet(
|
return BitcoinCashWallet(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: await Mnemonic.toSeed(mnemonic),
|
seedBytes: await Mnemonic.toSeed(mnemonic),
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex);
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
|
addressPageType: addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<BitcoinCashWallet> open({
|
static Future<BitcoinCashWallet> open({
|
||||||
|
@ -90,17 +94,20 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
final snp = await ElectrumWallletSnapshot.load(name, walletInfo.type, password);
|
final snp = await ElectrumWalletSnapshot.load(
|
||||||
|
name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
|
||||||
return BitcoinCashWallet(
|
return BitcoinCashWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: snp.addresses,
|
initialAddresses: snp.addresses,
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await Mnemonic.toSeed(snp.mnemonic),
|
seedBytes: await Mnemonic.toSeed(snp.mnemonic),
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex);
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
|
addressPageType: snp.addressPageType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -270,20 +277,18 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
electrumClient: electrumClient, amount: amount, fee: fee);
|
electrumClient: electrumClient, amount: amount, fee: fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
bitbox.ECPair generateKeyPair(
|
bitbox.ECPair generateKeyPair({required bitcoin.HDWallet hd, required int index}) =>
|
||||||
{required bitcoin.HDWallet hd,
|
|
||||||
required int index}) =>
|
|
||||||
bitbox.ECPair.fromWIF(hd.derive(index).wif!);
|
bitbox.ECPair.fromWIF(hd.derive(index).wif!);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int feeAmountForPriority(
|
int feeAmountForPriority(BitcoinTransactionPriority priority, int inputsCount, int outputsCount,
|
||||||
BitcoinTransactionPriority priority, int inputsCount, int outputsCount) =>
|
{int? size}) =>
|
||||||
feeRate(priority) * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
feeRate(priority) * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
||||||
|
|
||||||
int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount) =>
|
int feeAmountWithFeeRate(int feeRate, int inputsCount, int outputsCount, {int? size}) =>
|
||||||
feeRate * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
feeRate * bitbox.BitcoinCash.getByteCount(inputsCount, outputsCount);
|
||||||
|
|
||||||
int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount}) {
|
int calculateEstimatedFeeWithFeeRate(int feeRate, int? amount, {int? outputsCount, int? size}) {
|
||||||
int inputsCount = 0;
|
int inputsCount = 0;
|
||||||
int totalValue = 0;
|
int totalValue = 0;
|
||||||
|
|
||||||
|
@ -323,9 +328,10 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address = null}) {
|
String signMessage(String message, {String? address = null}) {
|
||||||
final index = address != null
|
final index = address != null
|
||||||
? walletAddresses.addresses
|
? walletAddresses.allAddresses
|
||||||
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
|
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
|
||||||
.index : null;
|
.index
|
||||||
|
: null;
|
||||||
final HD = index == null ? hd : hd.derive(index);
|
final HD = index == null ? hd : hd.derive(index);
|
||||||
return base64Encode(HD.signMessage(message));
|
return base64Encode(HD.signMessage(message));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum.dart';
|
|
||||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||||
import 'package:cw_bitcoin/utils.dart';
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
@ -11,24 +10,19 @@ part 'bitcoin_cash_wallet_addresses.g.dart';
|
||||||
class BitcoinCashWalletAddresses = BitcoinCashWalletAddressesBase with _$BitcoinCashWalletAddresses;
|
class BitcoinCashWalletAddresses = BitcoinCashWalletAddressesBase with _$BitcoinCashWalletAddresses;
|
||||||
|
|
||||||
abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
||||||
BitcoinCashWalletAddressesBase(WalletInfo walletInfo,
|
BitcoinCashWalletAddressesBase(
|
||||||
{required bitcoin.HDWallet mainHd,
|
WalletInfo walletInfo, {
|
||||||
required bitcoin.HDWallet sideHd,
|
required super.mainHd,
|
||||||
required bitcoin.NetworkType networkType,
|
required super.sideHd,
|
||||||
required ElectrumClient electrumClient,
|
required super.network,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
required super.electrumClient,
|
||||||
int initialRegularAddressIndex = 0,
|
super.initialAddresses,
|
||||||
int initialChangeAddressIndex = 0})
|
super.initialRegularAddressIndex,
|
||||||
: super(walletInfo,
|
super.initialChangeAddressIndex,
|
||||||
initialAddresses: initialAddresses,
|
}) : super(walletInfo);
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
|
||||||
mainHd: mainHd,
|
|
||||||
sideHd: sideHd,
|
|
||||||
electrumClient: electrumClient,
|
|
||||||
networkType: networkType);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress({required int index, required bitcoin.HDWallet hd}) =>
|
String getAddress(
|
||||||
generateP2PKHAddress(hd: hd, index: index, networkType: networkType);
|
{required int index, required bitcoin.HDWallet hd, BitcoinAddressType? addressType}) =>
|
||||||
|
generateP2PKHAddress(hd: hd, index: index, network: network);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,7 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:bip39/bip39.dart';
|
import 'package:bip39/bip39.dart';
|
||||||
import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart';
|
import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart';
|
||||||
import 'package:cw_core/balance.dart';
|
|
||||||
import 'package:cw_core/pathForWallet.dart';
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
import 'package:cw_core/transaction_history.dart';
|
|
||||||
import 'package:cw_core/transaction_info.dart';
|
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
@ -15,8 +12,7 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
|
class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
|
||||||
BitcoinCashRestoreWalletFromSeedCredentials,
|
BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials> {
|
||||||
BitcoinCashRestoreWalletFromWIFCredentials> {
|
|
||||||
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -30,13 +26,9 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
File(await pathForWallet(name: name, type: getType())).existsSync();
|
File(await pathForWallet(name: name, type: getType())).existsSync();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinCashWallet> create(
|
Future<BitcoinCashWallet> create(credentials, {bool? isTestnet}) async {
|
||||||
credentials) async {
|
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||||
final strength = (credentials.seedPhraseLength == 12)
|
|
||||||
? 128
|
|
||||||
: (credentials.seedPhraseLength == 24)
|
|
||||||
? 256
|
|
||||||
: 128;
|
|
||||||
final wallet = await BitcoinCashWalletBase.create(
|
final wallet = await BitcoinCashWalletBase.create(
|
||||||
mnemonic: await Mnemonic.generate(strength: strength),
|
mnemonic: await Mnemonic.generate(strength: strength),
|
||||||
password: credentials.password!,
|
password: credentials.password!,
|
||||||
|
@ -49,21 +41,25 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinCashWallet> openWallet(String name, String password) async {
|
Future<BitcoinCashWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
final walletInfo = walletInfoSource.values
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final wallet = await BitcoinCashWalletBase.open(
|
final wallet = await BitcoinCashWalletBase.open(
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
password: password,
|
||||||
|
name: name,
|
||||||
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
saveBackup(name);
|
saveBackup(name);
|
||||||
return wallet;
|
return wallet;
|
||||||
} catch(_) {
|
} catch (_) {
|
||||||
await restoreWalletFilesFromBackup(name);
|
await restoreWalletFilesFromBackup(name);
|
||||||
final wallet = await BitcoinCashWalletBase.open(
|
final wallet = await BitcoinCashWalletBase.open(
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
password: password,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
name: name,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
@ -71,17 +67,16 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> remove(String wallet) async {
|
Future<void> remove(String wallet) async {
|
||||||
File(await pathForWalletDir(name: wallet, type: getType()))
|
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
|
||||||
.delete(recursive: true);
|
final walletInfo = walletInfoSource.values
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(String currentName, String password, String newName) async {
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
|
final currentWalletInfo = walletInfoSource.values
|
||||||
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||||
final currentWallet = await BitcoinCashWalletBase.open(
|
final currentWallet = await BitcoinCashWalletBase.open(
|
||||||
password: password,
|
password: password,
|
||||||
name: currentName,
|
name: currentName,
|
||||||
|
@ -99,15 +94,14 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinCashWallet>
|
Future<BitcoinCashWallet> restoreFromKeys(credentials, {bool? isTestnet}) {
|
||||||
restoreFromKeys(credentials) {
|
|
||||||
// TODO: implement restoreFromKeys
|
// TODO: implement restoreFromKeys
|
||||||
throw UnimplementedError('restoreFromKeys() is not implemented');
|
throw UnimplementedError('restoreFromKeys() is not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinCashWallet> restoreFromSeed(
|
Future<BitcoinCashWallet> restoreFromSeed(BitcoinCashRestoreWalletFromSeedCredentials credentials,
|
||||||
BitcoinCashRestoreWalletFromSeedCredentials credentials) async {
|
{bool? isTestnet}) async {
|
||||||
if (!validateMnemonic(credentials.mnemonic)) {
|
if (!validateMnemonic(credentials.mnemonic)) {
|
||||||
throw BitcoinCashMnemonicIsIncorrectException();
|
throw BitcoinCashMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,10 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitbox-flutter.git
|
url: https://github.com/cake-tech/bitbox-flutter.git
|
||||||
ref: master
|
ref: master
|
||||||
bitcoin_base: ^3.0.1
|
bitcoin_base:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/bitcoin_base.git
|
||||||
|
ref: cake-update-v1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
13
cw_core/lib/enumerate.dart
Normal file
13
cw_core/lib/enumerate.dart
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
abstract class Enumerate {
|
||||||
|
String get value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
operator ==(other) {
|
||||||
|
if (identical(other, this)) return true;
|
||||||
|
if (other is! Enumerate) return false;
|
||||||
|
return other.runtimeType == runtimeType && value == other.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => value.hashCode;
|
||||||
|
}
|
21
cw_core/lib/receive_page_option.dart
Normal file
21
cw_core/lib/receive_page_option.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:cw_core/enumerate.dart';
|
||||||
|
|
||||||
|
class ReceivePageOption implements Enumerate {
|
||||||
|
static const mainnet = ReceivePageOption._('mainnet');
|
||||||
|
static const anonPayInvoice = ReceivePageOption._('anonPayInvoice');
|
||||||
|
static const anonPayDonationLink = ReceivePageOption._('anonPayDonationLink');
|
||||||
|
|
||||||
|
const ReceivePageOption._(this.value);
|
||||||
|
|
||||||
|
final String value;
|
||||||
|
|
||||||
|
String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReceivePageOptions = [
|
||||||
|
ReceivePageOption.mainnet,
|
||||||
|
ReceivePageOption.anonPayInvoice,
|
||||||
|
ReceivePageOption.anonPayDonationLink
|
||||||
|
];
|
|
@ -88,4 +88,6 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||||
Future<void> renameWalletFiles(String newWalletName);
|
Future<void> renameWalletFiles(String newWalletName);
|
||||||
|
|
||||||
String signMessage(String message, {String? address = null}) => throw UnimplementedError();
|
String signMessage(String message, {String? address = null}) => throw UnimplementedError();
|
||||||
|
|
||||||
|
bool? isTestnet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,12 @@ class WalletInfo extends HiveObject {
|
||||||
@HiveField(17)
|
@HiveField(17)
|
||||||
String? derivationPath;
|
String? derivationPath;
|
||||||
|
|
||||||
|
@HiveField(18)
|
||||||
|
String? addressPageType;
|
||||||
|
|
||||||
|
@HiveField(19)
|
||||||
|
String? network;
|
||||||
|
|
||||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||||
|
|
||||||
set yatLastUsedAddress(String address) {
|
set yatLastUsedAddress(String address) {
|
||||||
|
|
|
@ -9,11 +9,11 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
|
||||||
RFK extends WalletCredentials> {
|
RFK extends WalletCredentials> {
|
||||||
WalletType getType();
|
WalletType getType();
|
||||||
|
|
||||||
Future<WalletBase> create(N credentials);
|
Future<WalletBase> create(N credentials, {bool? isTestnet});
|
||||||
|
|
||||||
Future<WalletBase> restoreFromSeed(RFS credentials);
|
Future<WalletBase> restoreFromSeed(RFS credentials, {bool? isTestnet});
|
||||||
|
|
||||||
Future<WalletBase> restoreFromKeys(RFK credentials);
|
Future<WalletBase> restoreFromKeys(RFK credentials, {bool? isTestnet});
|
||||||
|
|
||||||
Future<WalletBase> openWallet(String name, String password);
|
Future<WalletBase> openWallet(String name, String password);
|
||||||
|
|
||||||
|
|
|
@ -5,34 +5,34 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
|
sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "47.0.0"
|
version: "64.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
|
sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.7.0"
|
version: "6.2.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: args
|
name: args
|
||||||
sha256: "139d809800a412ebb26a3892da228b2d0ba36f0ef5d9a82166e5e52ec8d61611"
|
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.2"
|
version: "2.4.2"
|
||||||
asn1lib:
|
asn1lib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: ab96a1cb3beeccf8145c52e449233fe68364c9641623acd3adad66f8184f1039
|
sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.5.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -53,10 +53,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.4.1"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -69,34 +69,34 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "4.0.1"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
|
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.4.2"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.3"
|
version: "2.4.8"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
|
sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.2.7"
|
version: "7.2.11"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -109,10 +109,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: built_value
|
name: built_value
|
||||||
sha256: "169565c8ad06adb760c3645bf71f00bff161b00002cace266cad42c5d22a7725"
|
sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.3"
|
version: "8.8.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -125,10 +125,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: checked_yaml
|
name: checked_yaml
|
||||||
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
|
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.3"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -141,10 +141,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: code_builder
|
name: code_builder
|
||||||
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
|
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "4.10.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -165,26 +165,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.3.4"
|
||||||
encrypt:
|
encrypt:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: encrypt
|
name: encrypt
|
||||||
sha256: "4fd4e4fdc21b9d7d4141823e1e6515cd94e7b8d84749504c232999fba25d9bbb"
|
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.1"
|
version: "5.0.3"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -197,10 +197,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.1.0"
|
||||||
file:
|
file:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -226,10 +226,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_mobx
|
name: flutter_mobx
|
||||||
sha256: "0da4add0016387a7bf309a0d0c41d36c6b3ae25ed7a176409267f166509e723e"
|
sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.6+5"
|
version: "2.2.0+2"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -247,18 +247,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: glob
|
name: glob
|
||||||
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
|
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
graphs:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: graphs
|
name: graphs
|
||||||
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
|
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.3.1"
|
||||||
hive:
|
hive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -327,18 +327,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: json_annotation
|
name: json_annotation
|
||||||
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.0"
|
version: "4.8.1"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: logging
|
name: logging
|
||||||
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
|
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.2.0"
|
||||||
matcher:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -375,18 +375,26 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mobx
|
name: mobx
|
||||||
sha256: f1862bd92c6a903fab67338f27e2f731117c3cb9ea37cee1a487f9e4e0de314a
|
sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3+1"
|
version: "2.3.0+1"
|
||||||
mobx_codegen:
|
mobx_codegen:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: mobx_codegen
|
name: mobx_codegen
|
||||||
sha256: "86122e410d8ea24dda0c69adb5c2a6ccadd5ce02ad46e144764e0d0184a06181"
|
sha256: b26c7f9c20b38f0ea572c1ed3f29d8e027cb265538bbd1aed3ec198642cfca42
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.6.0+1"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -407,26 +415,26 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
|
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.2"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -439,10 +447,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -455,26 +463,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.4"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.8"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
|
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.6.2"
|
version: "3.7.4"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -483,46 +491,46 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.1"
|
||||||
process:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: process
|
name: provider
|
||||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "6.1.1"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
|
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
pubspec_parse:
|
pubspec_parse:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a"
|
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.3"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
|
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
|
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.4"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -540,18 +548,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
|
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.6"
|
version: "1.5.0"
|
||||||
source_helper:
|
source_helper:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.4"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -620,10 +628,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: typed_data
|
name: typed_data
|
||||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.2"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -636,42 +644,42 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: watcher
|
name: watcher
|
||||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.1.0"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
|
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.4.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "5.0.9"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
|
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+3"
|
version: "1.0.4"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
|
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.0.0 <4.0.0"
|
dart: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=3.7.0"
|
flutter: ">=3.10.0"
|
||||||
|
|
|
@ -16,7 +16,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
WalletType getType() => WalletType.ethereum;
|
WalletType getType() => WalletType.ethereum;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<EthereumWallet> create(EVMChainNewWalletCredentials credentials) async {
|
Future<EthereumWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||||
|
|
||||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||||
|
@ -52,7 +52,6 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
saveBackup(name);
|
saveBackup(name);
|
||||||
return wallet;
|
return wallet;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
|
||||||
await restoreWalletFilesFromBackup(name);
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
|
||||||
final wallet = await EthereumWallet.open(
|
final wallet = await EthereumWallet.open(
|
||||||
|
@ -84,7 +83,8 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<EthereumWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials) async {
|
Future<EthereumWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
final wallet = EthereumWallet(
|
final wallet = EthereumWallet(
|
||||||
password: credentials.password!,
|
password: credentials.password!,
|
||||||
privateKey: credentials.privateKey,
|
privateKey: credentials.privateKey,
|
||||||
|
@ -100,8 +100,8 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<EthereumWallet> restoreFromSeed(
|
Future<EthereumWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
|
||||||
EVMChainRestoreWalletFromSeedCredentials credentials) async {
|
{bool? isTestnet}) async {
|
||||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||||
throw EthereumMnemonicIsIncorrectException();
|
throw EthereumMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
|
||||||
WalletType getType();
|
WalletType getType();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<T> create(EVMChainNewWalletCredentials credentials);
|
Future<T> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<T> openWallet(String name, String password);
|
Future<T> openWallet(String name, String password);
|
||||||
|
@ -31,10 +31,10 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
|
||||||
Future<void> rename(String currentName, String password, String newName);
|
Future<void> rename(String currentName, String password, String newName);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<T> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials);
|
Future<T> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials, {bool? isTestnet});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<T> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials);
|
Future<T> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials, {bool? isTestnet});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> isWalletExit(String name) async =>
|
Future<bool> isWalletExit(String name) async =>
|
||||||
|
|
|
@ -68,7 +68,7 @@ class HavenWalletService extends WalletService<
|
||||||
WalletType getType() => WalletType.haven;
|
WalletType getType() => WalletType.haven;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<HavenWallet> create(HavenNewWalletCredentials credentials) async {
|
Future<HavenWallet> create(HavenNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
await haven_wallet_manager.createWallet(
|
await haven_wallet_manager.createWallet(
|
||||||
|
@ -174,7 +174,7 @@ class HavenWalletService extends WalletService<
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<HavenWallet> restoreFromKeys(
|
Future<HavenWallet> restoreFromKeys(
|
||||||
HavenRestoreWalletFromKeysCredentials credentials) async {
|
HavenRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
await haven_wallet_manager.restoreFromKeys(
|
await haven_wallet_manager.restoreFromKeys(
|
||||||
|
@ -198,7 +198,7 @@ class HavenWalletService extends WalletService<
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<HavenWallet> restoreFromSeed(
|
Future<HavenWallet> restoreFromSeed(
|
||||||
HavenRestoreWalletFromSeedCredentials credentials) async {
|
HavenRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
await haven_wallet_manager.restoreFromSeed(
|
await haven_wallet_manager.restoreFromSeed(
|
||||||
|
|
|
@ -68,7 +68,7 @@ class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||||
WalletType getType() => WalletType.monero;
|
WalletType getType() => WalletType.monero;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async {
|
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
|
|
||||||
|
@ -203,7 +203,8 @@ class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials) async {
|
Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
await monero_wallet_manager.restoreFromKeys(
|
await monero_wallet_manager.restoreFromKeys(
|
||||||
|
@ -227,7 +228,8 @@ class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials) async {
|
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
// Restore from Polyseed
|
// Restore from Polyseed
|
||||||
if (Polyseed.isValidSeed(credentials.mnemonic)) {
|
if (Polyseed.isValidSeed(credentials.mnemonic)) {
|
||||||
return restoreFromPolyseed(credentials);
|
return restoreFromPolyseed(credentials);
|
||||||
|
|
|
@ -26,7 +26,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
WalletType getType() => WalletType.nano;
|
WalletType getType() => WalletType.nano;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<WalletBase> create(NanoNewWalletCredentials credentials) async {
|
Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
// nano standard:
|
// nano standard:
|
||||||
DerivationType derivationType = DerivationType.nano;
|
DerivationType derivationType = DerivationType.nano;
|
||||||
String seedKey = NanoSeeds.generateSeed();
|
String seedKey = NanoSeeds.generateSeed();
|
||||||
|
@ -79,7 +79,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials) async {
|
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
||||||
if (credentials.seedKey.contains(' ')) {
|
if (credentials.seedKey.contains(' ')) {
|
||||||
throw Exception("Invalid key!");
|
throw Exception("Invalid key!");
|
||||||
} else {
|
} else {
|
||||||
|
@ -113,7 +113,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials) async {
|
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||||
if (credentials.mnemonic.contains(' ')) {
|
if (credentials.mnemonic.contains(' ')) {
|
||||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||||
throw nm.NanoMnemonicIsIncorrectException();
|
throw nm.NanoMnemonicIsIncorrectException();
|
||||||
|
|
|
@ -19,7 +19,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
WalletType getType() => WalletType.polygon;
|
WalletType getType() => WalletType.polygon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PolygonWallet> create(EVMChainNewWalletCredentials credentials) async {
|
Future<PolygonWallet> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
|
||||||
|
|
||||||
final mnemonic = bip39.generateMnemonic(strength: strength);
|
final mnemonic = bip39.generateMnemonic(strength: strength);
|
||||||
|
@ -62,7 +62,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
);
|
);
|
||||||
|
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -70,8 +70,8 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PolygonWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials) async {
|
Future<PolygonWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
final wallet = PolygonWallet(
|
final wallet = PolygonWallet(
|
||||||
password: credentials.password!,
|
password: credentials.password!,
|
||||||
privateKey: credentials.privateKey,
|
privateKey: credentials.privateKey,
|
||||||
|
@ -87,8 +87,8 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PolygonWallet> restoreFromSeed(
|
Future<PolygonWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
|
||||||
EVMChainRestoreWalletFromSeedCredentials credentials) async {
|
{bool? isTestnet}) async {
|
||||||
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
if (!bip39.validateMnemonic(credentials.mnemonic)) {
|
||||||
throw PolygonMnemonicIsIncorrectException();
|
throw PolygonMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,181 +1,191 @@
|
||||||
part of 'bitcoin.dart';
|
part of 'bitcoin.dart';
|
||||||
|
|
||||||
class CWBitcoin extends Bitcoin {
|
class CWBitcoin extends Bitcoin {
|
||||||
@override
|
@override
|
||||||
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
|
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
|
||||||
|
|
||||||
@override
|
|
||||||
WalletCredentials createBitcoinRestoreWalletFromSeedCredentials({
|
|
||||||
required String name,
|
|
||||||
required String mnemonic,
|
|
||||||
required String password})
|
|
||||||
=> BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password);
|
|
||||||
|
|
||||||
@override
|
|
||||||
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({
|
|
||||||
required String name,
|
|
||||||
required String password,
|
|
||||||
required String wif,
|
|
||||||
WalletInfo? walletInfo})
|
|
||||||
=> BitcoinRestoreWalletFromWIFCredentials(name: name, password: password, wif: wif, walletInfo: walletInfo);
|
|
||||||
|
|
||||||
@override
|
|
||||||
WalletCredentials createBitcoinNewWalletCredentials({
|
|
||||||
required String name,
|
|
||||||
WalletInfo? walletInfo})
|
|
||||||
=> BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> getWordList() => wordlist;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, String> getWalletKeys(Object wallet) {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
final keys = bitcoinWallet.keys;
|
|
||||||
|
|
||||||
return <String, String>{
|
|
||||||
'wif': keys.wif,
|
|
||||||
'privateKey': keys.privateKey,
|
|
||||||
'publicKey': keys.publicKey
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<TransactionPriority> getTransactionPriorities()
|
|
||||||
=> BitcoinTransactionPriority.all;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<TransactionPriority> getLitecoinTransactionPriorities()
|
|
||||||
=> LitecoinTransactionPriority.all;
|
|
||||||
|
|
||||||
@override
|
|
||||||
TransactionPriority deserializeBitcoinTransactionPriority(int raw)
|
|
||||||
=> BitcoinTransactionPriority.deserialize(raw: raw);
|
|
||||||
|
|
||||||
@override
|
|
||||||
TransactionPriority deserializeLitecoinTransactionPriority(int raw)
|
|
||||||
=> LitecoinTransactionPriority.deserialize(raw: raw);
|
|
||||||
|
|
||||||
@override
|
|
||||||
int getFeeRate(Object wallet, TransactionPriority priority) {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
return bitcoinWallet.feeRate(priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> generateNewAddress(Object wallet, String label) async {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
await bitcoinWallet.walletAddresses.generateNewAddress(label: label, hd: bitcoinWallet.hd);
|
|
||||||
await wallet.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> updateAddress(Object wallet,String address, String label) async {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
bitcoinWallet.walletAddresses.updateAddress(address, label);
|
|
||||||
await wallet.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate})
|
|
||||||
=> BitcoinTransactionCredentials(
|
|
||||||
outputs.map((out) => OutputInfo(
|
|
||||||
fiatAmount: out.fiatAmount,
|
|
||||||
cryptoAmount: out.cryptoAmount,
|
|
||||||
address: out.address,
|
|
||||||
note: out.note,
|
|
||||||
sendAll: out.sendAll,
|
|
||||||
extractedAddress: out.extractedAddress,
|
|
||||||
isParsedAddress: out.isParsedAddress,
|
|
||||||
formattedCryptoAmount: out.formattedCryptoAmount))
|
|
||||||
.toList(),
|
|
||||||
priority: priority as BitcoinTransactionPriority,
|
|
||||||
feeRate: feeRate);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate})
|
|
||||||
=> BitcoinTransactionCredentials(
|
|
||||||
outputs,
|
|
||||||
priority: priority != null ? priority as BitcoinTransactionPriority : null,
|
|
||||||
feeRate: feeRate);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<String> getAddresses(Object wallet) {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
return bitcoinWallet.walletAddresses.addresses
|
|
||||||
.map((BitcoinAddressRecord addr) => addr.address)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
@computed
|
|
||||||
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
|
|
||||||
final electrumWallet = wallet as ElectrumWallet;
|
|
||||||
return electrumWallet.walletAddresses.addresses
|
|
||||||
.map((BitcoinAddressRecord addr) => ElectrumSubAddress(
|
|
||||||
id: addr.index,
|
|
||||||
name: addr.name,
|
|
||||||
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
|
|
||||||
txCount: addr.txCount,
|
|
||||||
balance: addr.balance,
|
|
||||||
isChange: addr.isHidden))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String getAddress(Object wallet) {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
return bitcoinWallet.walletAddresses.address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String formatterBitcoinAmountToString({required int amount})
|
|
||||||
=> bitcoinAmountToString(amount: amount);
|
|
||||||
|
|
||||||
@override
|
|
||||||
double formatterBitcoinAmountToDouble({required int amount})
|
|
||||||
=> bitcoinAmountToDouble(amount: amount);
|
|
||||||
|
|
||||||
@override
|
|
||||||
int formatterStringDoubleToBitcoinAmount(String amount)
|
|
||||||
=> stringDoubleToBitcoinAmount(amount);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate)
|
WalletCredentials createBitcoinRestoreWalletFromSeedCredentials(
|
||||||
=> (priority as BitcoinTransactionPriority).labelWithRate(rate);
|
{required String name, required String mnemonic, required String password}) =>
|
||||||
|
BitcoinRestoreWalletFromSeedCredentials(name: name, mnemonic: mnemonic, password: password);
|
||||||
@override
|
|
||||||
List<BitcoinUnspent> getUnspents(Object wallet) {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
return bitcoinWallet.unspentCoins;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateUnspents(Object wallet) async {
|
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
|
||||||
await bitcoinWallet.updateUnspent();
|
|
||||||
}
|
|
||||||
|
|
||||||
WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
|
|
||||||
return BitcoinWalletService(walletInfoSource, unspentCoinSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
WalletService createLitecoinWalletService(Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
|
|
||||||
return LitecoinWalletService(walletInfoSource, unspentCoinSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TransactionPriority getBitcoinTransactionPriorityMedium()
|
|
||||||
=> BitcoinTransactionPriority.medium;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TransactionPriority getLitecoinTransactionPriorityMedium()
|
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials(
|
||||||
=> LitecoinTransactionPriority.medium;
|
{required String name,
|
||||||
|
required String password,
|
||||||
|
required String wif,
|
||||||
|
WalletInfo? walletInfo}) =>
|
||||||
|
BitcoinRestoreWalletFromWIFCredentials(
|
||||||
|
name: name, password: password, wif: wif, walletInfo: walletInfo);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TransactionPriority getBitcoinTransactionPrioritySlow()
|
WalletCredentials createBitcoinNewWalletCredentials(
|
||||||
=> BitcoinTransactionPriority.slow;
|
{required String name, WalletInfo? walletInfo}) =>
|
||||||
|
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TransactionPriority getLitecoinTransactionPrioritySlow()
|
List<String> getWordList() => wordlist;
|
||||||
=> LitecoinTransactionPriority.slow;
|
|
||||||
}
|
@override
|
||||||
|
Map<String, String> getWalletKeys(Object wallet) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
final keys = bitcoinWallet.keys;
|
||||||
|
|
||||||
|
return <String, String>{
|
||||||
|
'wif': keys.wif,
|
||||||
|
'privateKey': keys.privateKey,
|
||||||
|
'publicKey': keys.publicKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<TransactionPriority> getTransactionPriorities() => BitcoinTransactionPriority.all;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<TransactionPriority> getLitecoinTransactionPriorities() => LitecoinTransactionPriority.all;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TransactionPriority deserializeBitcoinTransactionPriority(int raw) =>
|
||||||
|
BitcoinTransactionPriority.deserialize(raw: raw);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TransactionPriority deserializeLitecoinTransactionPriority(int raw) =>
|
||||||
|
LitecoinTransactionPriority.deserialize(raw: raw);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int getFeeRate(Object wallet, TransactionPriority priority) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
return bitcoinWallet.feeRate(priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> generateNewAddress(Object wallet, String label) async {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
|
||||||
|
await wallet.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateAddress(Object wallet, String address, String label) async {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
bitcoinWallet.walletAddresses.updateAddress(address, label);
|
||||||
|
await wallet.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object createBitcoinTransactionCredentials(List<Output> outputs,
|
||||||
|
{required TransactionPriority priority, int? feeRate}) =>
|
||||||
|
BitcoinTransactionCredentials(
|
||||||
|
outputs
|
||||||
|
.map((out) => OutputInfo(
|
||||||
|
fiatAmount: out.fiatAmount,
|
||||||
|
cryptoAmount: out.cryptoAmount,
|
||||||
|
address: out.address,
|
||||||
|
note: out.note,
|
||||||
|
sendAll: out.sendAll,
|
||||||
|
extractedAddress: out.extractedAddress,
|
||||||
|
isParsedAddress: out.isParsedAddress,
|
||||||
|
formattedCryptoAmount: out.formattedCryptoAmount))
|
||||||
|
.toList(),
|
||||||
|
priority: priority as BitcoinTransactionPriority,
|
||||||
|
feeRate: feeRate);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs,
|
||||||
|
{TransactionPriority? priority, required int feeRate}) =>
|
||||||
|
BitcoinTransactionCredentials(outputs,
|
||||||
|
priority: priority != null ? priority as BitcoinTransactionPriority : null,
|
||||||
|
feeRate: feeRate);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> getAddresses(Object wallet) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
return bitcoinWallet.walletAddresses.addressesByReceiveType
|
||||||
|
.map((BitcoinAddressRecord addr) => addr.address)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@computed
|
||||||
|
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
|
||||||
|
final electrumWallet = wallet as ElectrumWallet;
|
||||||
|
return electrumWallet.walletAddresses.addressesByReceiveType
|
||||||
|
.map((BitcoinAddressRecord addr) => ElectrumSubAddress(
|
||||||
|
id: addr.index,
|
||||||
|
name: addr.name,
|
||||||
|
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
|
||||||
|
txCount: addr.txCount,
|
||||||
|
balance: addr.balance,
|
||||||
|
isChange: addr.isHidden))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getAddress(Object wallet) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
return bitcoinWallet.walletAddresses.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String formatterBitcoinAmountToString({required int amount}) =>
|
||||||
|
bitcoinAmountToString(amount: amount);
|
||||||
|
|
||||||
|
@override
|
||||||
|
double formatterBitcoinAmountToDouble({required int amount}) =>
|
||||||
|
bitcoinAmountToDouble(amount: amount);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int formatterStringDoubleToBitcoinAmount(String amount) => stringDoubleToBitcoinAmount(amount);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate) =>
|
||||||
|
(priority as BitcoinTransactionPriority).labelWithRate(rate);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<BitcoinUnspent> getUnspents(Object wallet) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
return bitcoinWallet.unspentCoins;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateUnspents(Object wallet) async {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
await bitcoinWallet.updateUnspent();
|
||||||
|
}
|
||||||
|
|
||||||
|
WalletService createBitcoinWalletService(
|
||||||
|
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
|
||||||
|
return BitcoinWalletService(walletInfoSource, unspentCoinSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
WalletService createLitecoinWalletService(
|
||||||
|
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
|
||||||
|
return LitecoinWalletService(walletInfoSource, unspentCoinSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TransactionPriority getBitcoinTransactionPriorityMedium() => BitcoinTransactionPriority.medium;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TransactionPriority getLitecoinTransactionPriorityMedium() => LitecoinTransactionPriority.medium;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TransactionPriority getBitcoinTransactionPrioritySlow() => BitcoinTransactionPriority.slow;
|
||||||
|
|
||||||
|
@override
|
||||||
|
TransactionPriority getLitecoinTransactionPrioritySlow() => LitecoinTransactionPriority.slow;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setAddressType(Object wallet, dynamic option) async {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
await bitcoinWallet.walletAddresses.setAddressType(option as BitcoinAddressType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
BitcoinReceivePageOption getSelectedAddressType(Object wallet) {
|
||||||
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
|
return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<BitcoinReceivePageOption> getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/core/validator.dart';
|
import 'package:cake_wallet/core/validator.dart';
|
||||||
import 'package:cake_wallet/solana/solana.dart';
|
import 'package:cake_wallet/solana/solana.dart';
|
||||||
|
@ -9,7 +9,9 @@ class AddressValidator extends TextValidator {
|
||||||
AddressValidator({required CryptoCurrency type})
|
AddressValidator({required CryptoCurrency type})
|
||||||
: super(
|
: super(
|
||||||
errorMessage: S.current.error_text_address,
|
errorMessage: S.current.error_text_address,
|
||||||
useAdditionalValidation: type == CryptoCurrency.btc ? bitcoin.Address.validateAddress : null,
|
useAdditionalValidation: type == CryptoCurrency.btc
|
||||||
|
? (String txt) => validateAddress(address: txt, network: BitcoinNetwork.mainnet)
|
||||||
|
: null,
|
||||||
pattern: getPattern(type),
|
pattern: getPattern(type),
|
||||||
length: getLength(type));
|
length: getLength(type));
|
||||||
|
|
||||||
|
@ -24,7 +26,7 @@ class AddressValidator extends TextValidator {
|
||||||
return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
|
return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$'
|
||||||
'|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
|
'|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
|
||||||
case CryptoCurrency.btc:
|
case CryptoCurrency.btc:
|
||||||
return '^3[0-9a-zA-Z]{32}\$|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{59}\$';
|
return '^${P2pkhAddress.regex.pattern}\$|^${P2shAddress.regex.pattern}\$|^${P2wpkhAddress.regex.pattern}\$|${P2trAddress.regex.pattern}\$|^${P2wshAddress.regex.pattern}\$';
|
||||||
case CryptoCurrency.nano:
|
case CryptoCurrency.nano:
|
||||||
return '[0-9a-zA-Z_]';
|
return '[0-9a-zA-Z_]';
|
||||||
case CryptoCurrency.banano:
|
case CryptoCurrency.banano:
|
||||||
|
@ -89,7 +91,7 @@ class AddressValidator extends TextValidator {
|
||||||
case CryptoCurrency.dai:
|
case CryptoCurrency.dai:
|
||||||
case CryptoCurrency.dash:
|
case CryptoCurrency.dash:
|
||||||
case CryptoCurrency.eos:
|
case CryptoCurrency.eos:
|
||||||
return '[0-9a-zA-Z]';
|
return '[0-9a-zA-Z]';
|
||||||
case CryptoCurrency.bch:
|
case CryptoCurrency.bch:
|
||||||
return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{42}\$|^bitcoincash:q|p[0-9a-zA-Z]{41}\$|^bitcoincash:q|p[0-9a-zA-Z]{42}\$';
|
return '^(?!bitcoincash:)[0-9a-zA-Z]*\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{41}\$|^(?!bitcoincash:)q|p[0-9a-zA-Z]{42}\$|^bitcoincash:q|p[0-9a-zA-Z]{41}\$|^bitcoincash:q|p[0-9a-zA-Z]{42}\$';
|
||||||
case CryptoCurrency.bnb:
|
case CryptoCurrency.bnb:
|
||||||
|
@ -268,12 +270,11 @@ class AddressValidator extends TextValidator {
|
||||||
'|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
|
'|([^0-9a-zA-Z]|^)8[0-9a-zA-Z]{94}([^0-9a-zA-Z]|\$)'
|
||||||
'|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)';
|
'|([^0-9a-zA-Z]|^)[0-9a-zA-Z]{106}([^0-9a-zA-Z]|\$)';
|
||||||
case CryptoCurrency.btc:
|
case CryptoCurrency.btc:
|
||||||
return '([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)'
|
return '([^0-9a-zA-Z]|^)${P2pkhAddress.regex.pattern}|\$)'
|
||||||
'|([^0-9a-zA-Z]|^)1[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)'
|
'([^0-9a-zA-Z]|^)${P2shAddress.regex.pattern}|\$)'
|
||||||
'|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{32}([^0-9a-zA-Z]|\$)'
|
'([^0-9a-zA-Z]|^)${P2wpkhAddress.regex.pattern}|\$)'
|
||||||
'|([^0-9a-zA-Z]|^)3[0-9a-zA-Z]{33}([^0-9a-zA-Z]|\$)'
|
'([^0-9a-zA-Z]|^)${P2wshAddress.regex.pattern}|\$)'
|
||||||
'|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{39}([^0-9a-zA-Z]|\$)'
|
'([^0-9a-zA-Z]|^)${P2trAddress.regex.pattern}|\$)';
|
||||||
'|([^0-9a-zA-Z]|^)bc1[0-9a-zA-Z]{59}([^0-9a-zA-Z]|\$)';
|
|
||||||
case CryptoCurrency.ltc:
|
case CryptoCurrency.ltc:
|
||||||
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
|
return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||||
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
|
'|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)'
|
||||||
|
@ -297,4 +298,4 @@ class AddressValidator extends TextValidator {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -55,7 +54,7 @@ class WalletCreationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<WalletBase> create(WalletCredentials credentials) async {
|
Future<WalletBase> create(WalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
checkIfExists(credentials.name);
|
checkIfExists(credentials.name);
|
||||||
final password = generateWalletPassword();
|
final password = generateWalletPassword();
|
||||||
credentials.password = password;
|
credentials.password = password;
|
||||||
|
@ -63,7 +62,7 @@ class WalletCreationService {
|
||||||
credentials.seedPhraseLength = settingsStore.seedPhraseLength.value;
|
credentials.seedPhraseLength = settingsStore.seedPhraseLength.value;
|
||||||
}
|
}
|
||||||
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
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) {
|
if (wallet.type == WalletType.monero) {
|
||||||
await sharedPreferences.setBool(
|
await sharedPreferences.setBool(
|
||||||
|
@ -73,12 +72,12 @@ class WalletCreationService {
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<WalletBase> restoreFromKeys(WalletCredentials credentials) async {
|
Future<WalletBase> restoreFromKeys(WalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
checkIfExists(credentials.name);
|
checkIfExists(credentials.name);
|
||||||
final password = generateWalletPassword();
|
final password = generateWalletPassword();
|
||||||
credentials.password = password;
|
credentials.password = password;
|
||||||
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
||||||
final wallet = await _service!.restoreFromKeys(credentials);
|
final wallet = await _service!.restoreFromKeys(credentials, isTestnet: isTestnet);
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
await sharedPreferences.setBool(
|
await sharedPreferences.setBool(
|
||||||
|
@ -88,12 +87,12 @@ class WalletCreationService {
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<WalletBase> restoreFromSeed(WalletCredentials credentials) async {
|
Future<WalletBase> restoreFromSeed(WalletCredentials credentials, {bool? isTestnet}) async {
|
||||||
checkIfExists(credentials.name);
|
checkIfExists(credentials.name);
|
||||||
final password = generateWalletPassword();
|
final password = generateWalletPassword();
|
||||||
credentials.password = password;
|
credentials.password = password;
|
||||||
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
||||||
final wallet = await _service!.restoreFromSeed(credentials);
|
final wallet = await _service!.restoreFromSeed(credentials, isTestnet: isTestnet);
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
await sharedPreferences.setBool(
|
await sharedPreferences.setBool(
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'package:cake_wallet/core/yat_service.dart';
|
||||||
import 'package:cake_wallet/entities/background_tasks.dart';
|
import 'package:cake_wallet/entities/background_tasks.dart';
|
||||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
import 'package:cake_wallet/nano/nano.dart';
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cake_wallet/ionia/ionia_anypay.dart';
|
import 'package:cake_wallet/ionia/ionia_anypay.dart';
|
||||||
|
|
|
@ -23,6 +23,10 @@ import 'package:collection/collection.dart';
|
||||||
|
|
||||||
const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081';
|
const newCakeWalletMoneroUri = 'xmr-node.cakewallet.com:18081';
|
||||||
const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
|
const cakeWalletBitcoinElectrumUri = 'electrum.cakewallet.com:50002';
|
||||||
|
const publicBitcoinTestnetElectrumAddress = 'electrum.blockstream.info';
|
||||||
|
const publicBitcoinTestnetElectrumPort = '60002';
|
||||||
|
const publicBitcoinTestnetElectrumUri =
|
||||||
|
'$publicBitcoinTestnetElectrumAddress:$publicBitcoinTestnetElectrumPort';
|
||||||
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
|
const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002';
|
||||||
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
|
const havenDefaultNodeUri = 'nodes.havenprotocol.org:443';
|
||||||
const ethereumDefaultNodeUri = 'ethereum.publicnode.com';
|
const ethereumDefaultNodeUri = 'ethereum.publicnode.com';
|
||||||
|
@ -334,6 +338,12 @@ Node? getBitcoinDefaultElectrumServer({required Box<Node> nodes}) {
|
||||||
nodes.values.firstWhereOrNull((node) => node.type == WalletType.bitcoin);
|
nodes.values.firstWhereOrNull((node) => node.type == WalletType.bitcoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node? getBitcoinTestnetDefaultElectrumServer({required Box<Node> nodes}) {
|
||||||
|
return nodes.values
|
||||||
|
.firstWhereOrNull((Node node) => node.uriRaw == publicBitcoinTestnetElectrumUri) ??
|
||||||
|
nodes.values.firstWhereOrNull((node) => node.type == WalletType.bitcoin);
|
||||||
|
}
|
||||||
|
|
||||||
Node? getLitecoinDefaultElectrumServer({required Box<Node> nodes}) {
|
Node? getLitecoinDefaultElectrumServer({required Box<Node> nodes}) {
|
||||||
return nodes.values
|
return nodes.values
|
||||||
.firstWhereOrNull((Node node) => node.uriRaw == cakeWalletLitecoinElectrumUri) ??
|
.firstWhereOrNull((Node node) => node.uriRaw == cakeWalletLitecoinElectrumUri) ??
|
||||||
|
@ -503,8 +513,15 @@ Future<void> rewriteSecureStoragePin({required FlutterSecureStorage secureStorag
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
||||||
{required SharedPreferences sharedPreferences, required Box<Node> nodes}) async {
|
{required SharedPreferences sharedPreferences,
|
||||||
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
required Box<Node> nodes,
|
||||||
|
bool? isTestnet}) async {
|
||||||
|
Node? server;
|
||||||
|
if (isTestnet == true) {
|
||||||
|
server = getBitcoinTestnetDefaultElectrumServer(nodes: nodes);
|
||||||
|
} else {
|
||||||
|
server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
||||||
|
}
|
||||||
final serverId = server?.key as int? ?? 0;
|
final serverId = server?.key as int? ?? 0;
|
||||||
|
|
||||||
await sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, serverId);
|
await sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, serverId);
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
|
|
||||||
enum ReceivePageOption {
|
|
||||||
mainnet,
|
|
||||||
anonPayInvoice,
|
|
||||||
anonPayDonationLink;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
String label = '';
|
|
||||||
switch (this) {
|
|
||||||
case ReceivePageOption.mainnet:
|
|
||||||
label = 'Mainnet';
|
|
||||||
break;
|
|
||||||
case ReceivePageOption.anonPayInvoice:
|
|
||||||
label = 'Trocador AnonPay Invoice';
|
|
||||||
break;
|
|
||||||
case ReceivePageOption.anonPayDonationLink:
|
|
||||||
label = 'Trocador AnonPay Donation Link';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -532,13 +532,19 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
builder: (_) => getIt.get<WebViewPage>(param1: title, param2: url));
|
builder: (_) => getIt.get<WebViewPage>(param1: title, param2: url));
|
||||||
|
|
||||||
case Routes.advancedPrivacySettings:
|
case Routes.advancedPrivacySettings:
|
||||||
final type = settings.arguments as WalletType;
|
final args = settings.arguments as Map<String, dynamic>;
|
||||||
|
final type = args['type'] as WalletType;
|
||||||
|
final useTestnet = args['useTestnet'] as bool;
|
||||||
|
final toggleTestnet = args['toggleTestnet'] as Function(bool? val);
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => AdvancedPrivacySettingsPage(
|
builder: (_) => AdvancedPrivacySettingsPage(
|
||||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
useTestnet,
|
||||||
getIt.get<NodeCreateOrEditViewModel>(param1: type, param2: false),
|
toggleTestnet,
|
||||||
getIt.get<SeedTypeViewModel>()));
|
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
||||||
|
getIt.get<NodeCreateOrEditViewModel>(param1: type, param2: false),
|
||||||
|
getIt.get<SeedTypeViewModel>(),
|
||||||
|
));
|
||||||
|
|
||||||
case Routes.anonPayInvoicePage:
|
case Routes.anonPayInvoicePage:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
|
|
|
@ -5,7 +5,8 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||||
import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
|
import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
|
import 'package:cw_bitcoin/bitcoin_receive_page_option.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
|
||||||
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
import 'package:cake_wallet/src/widgets/gradient_background.dart';
|
||||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||||
|
@ -27,6 +28,7 @@ import 'package:keyboard_actions/keyboard_actions.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
|
|
||||||
class AddressPage extends BasePage {
|
class AddressPage extends BasePage {
|
||||||
AddressPage({
|
AddressPage({
|
||||||
|
@ -69,7 +71,7 @@ class AddressPage extends BasePage {
|
||||||
size: 16,
|
size: 16,
|
||||||
);
|
);
|
||||||
final _closeButton =
|
final _closeButton =
|
||||||
currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
|
currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
|
||||||
|
|
||||||
bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
|
bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
|
||||||
|
|
||||||
|
@ -163,11 +165,10 @@ class AddressPage extends BasePage {
|
||||||
return SelectButton(
|
return SelectButton(
|
||||||
text: addressListViewModel.buttonTitle,
|
text: addressListViewModel.buttonTitle,
|
||||||
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled &&
|
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled &&
|
||||||
(WalletType.monero == addressListViewModel.wallet.type ||
|
(WalletType.monero == addressListViewModel.wallet.type ||
|
||||||
WalletType.haven == addressListViewModel.wallet.type)
|
WalletType.haven == addressListViewModel.wallet.type)
|
||||||
? await showPopUp<void>(
|
? await showPopUp<void>(
|
||||||
context: context,
|
context: context, builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||||
builder: (_) => getIt.get<MoneroAccountListPage>())
|
|
||||||
: Navigator.of(context).pushNamed(Routes.receive),
|
: Navigator.of(context).pushNamed(Routes.receive),
|
||||||
textColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
textColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
|
||||||
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
|
||||||
|
@ -229,6 +230,21 @@ class AddressPage extends BasePage {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case BitcoinReceivePageOption.p2pkh:
|
||||||
|
addressListViewModel.setAddressType(P2pkhAddressType.p2pkh);
|
||||||
|
break;
|
||||||
|
case BitcoinReceivePageOption.p2sh:
|
||||||
|
addressListViewModel.setAddressType(P2shAddressType.p2wpkhInP2sh);
|
||||||
|
break;
|
||||||
|
case BitcoinReceivePageOption.p2wpkh:
|
||||||
|
addressListViewModel.setAddressType(SegwitAddresType.p2wpkh);
|
||||||
|
break;
|
||||||
|
case BitcoinReceivePageOption.p2tr:
|
||||||
|
addressListViewModel.setAddressType(SegwitAddresType.p2tr);
|
||||||
|
break;
|
||||||
|
case BitcoinReceivePageOption.p2wsh:
|
||||||
|
addressListViewModel.setAddressType(SegwitAddresType.p2wsh);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/default_settings_migration.dart';
|
||||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/seed_phrase_length.dart';
|
import 'package:cake_wallet/entities/seed_phrase_length.dart';
|
||||||
|
@ -11,6 +12,7 @@ import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.
|
||||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
|
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
|
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
@ -19,7 +21,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
|
|
||||||
class AdvancedPrivacySettingsPage extends BasePage {
|
class AdvancedPrivacySettingsPage extends BasePage {
|
||||||
AdvancedPrivacySettingsPage(
|
AdvancedPrivacySettingsPage(this.useTestnet, this.toggleUseTestnet,
|
||||||
this.advancedPrivacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel);
|
this.advancedPrivacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel);
|
||||||
|
|
||||||
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
|
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
|
||||||
|
@ -29,13 +31,16 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||||
@override
|
@override
|
||||||
String get title => S.current.privacy_settings;
|
String get title => S.current.privacy_settings;
|
||||||
|
|
||||||
|
final bool useTestnet;
|
||||||
|
final Function(bool? val) toggleUseTestnet;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) => AdvancedPrivacySettingsBody(
|
Widget body(BuildContext context) => AdvancedPrivacySettingsBody(useTestnet, toggleUseTestnet,
|
||||||
advancedPrivacySettingsViewModel, nodeViewModel, seedTypeViewModel);
|
advancedPrivacySettingsViewModel, nodeViewModel, seedTypeViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AdvancedPrivacySettingsBody extends StatefulWidget {
|
class AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||||
const AdvancedPrivacySettingsBody(
|
const AdvancedPrivacySettingsBody(this.useTestnet, this.toggleUseTestnet,
|
||||||
this.privacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel,
|
this.privacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel,
|
||||||
{Key? key})
|
{Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
@ -44,6 +49,9 @@ class AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||||
final NodeCreateOrEditViewModel nodeViewModel;
|
final NodeCreateOrEditViewModel nodeViewModel;
|
||||||
final SeedTypeViewModel seedTypeViewModel;
|
final SeedTypeViewModel seedTypeViewModel;
|
||||||
|
|
||||||
|
final bool useTestnet;
|
||||||
|
final Function(bool? val) toggleUseTestnet;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
|
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
|
||||||
}
|
}
|
||||||
|
@ -52,9 +60,14 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
|
||||||
_AdvancedPrivacySettingsBodyState();
|
_AdvancedPrivacySettingsBodyState();
|
||||||
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
bool? testnetValue;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (testnetValue == null && widget.useTestnet != null) {
|
||||||
|
testnetValue = widget.useTestnet;
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24),
|
||||||
child: ScrollableWithBottomSection(
|
child: ScrollableWithBottomSection(
|
||||||
|
@ -125,6 +138,19 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
if (widget.privacySettingsViewModel.type == WalletType.bitcoin)
|
||||||
|
Builder(builder: (_) {
|
||||||
|
final val = testnetValue!;
|
||||||
|
return SettingsSwitcherCell(
|
||||||
|
title: S.current.use_testnet,
|
||||||
|
value: val,
|
||||||
|
onValueChange: (_, __) {
|
||||||
|
setState(() {
|
||||||
|
testnetValue = !val;
|
||||||
|
});
|
||||||
|
widget.toggleUseTestnet!.call(testnetValue);
|
||||||
|
});
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
bottomSectionPadding: EdgeInsets.all(24),
|
bottomSectionPadding: EdgeInsets.all(24),
|
||||||
|
@ -137,6 +163,13 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
widget.nodeViewModel.save();
|
||||||
|
} else if (testnetValue == true) {
|
||||||
|
// TODO: add type (mainnet/testnet) to Node class so when switching wallets the node can be switched to a matching type
|
||||||
|
// Currently this is so you can create a working testnet wallet but you need to keep switching back the node if you use multiple wallets at once
|
||||||
|
widget.nodeViewModel.address = publicBitcoinTestnetElectrumAddress;
|
||||||
|
widget.nodeViewModel.port = publicBitcoinTestnetElectrumPort;
|
||||||
|
|
||||||
widget.nodeViewModel.save();
|
widget.nodeViewModel.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,9 @@ class NewWalletPage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) => WalletNameForm(
|
Widget body(BuildContext context) => WalletNameForm(
|
||||||
_walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage, _seedTypeViewModel);
|
_walletNewVM,
|
||||||
|
currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage,
|
||||||
|
_seedTypeViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletNameForm extends StatefulWidget {
|
class WalletNameForm extends StatefulWidget {
|
||||||
|
@ -187,7 +189,6 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (_walletNewVM.hasLanguageSelector) ...[
|
if (_walletNewVM.hasLanguageSelector) ...[
|
||||||
if (_walletNewVM.hasSeedType) ...[
|
if (_walletNewVM.hasSeedType) ...[
|
||||||
Observer(
|
Observer(
|
||||||
|
@ -222,7 +223,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -245,8 +246,11 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context)
|
Navigator.of(context).pushNamed(Routes.advancedPrivacySettings, arguments: {
|
||||||
.pushNamed(Routes.advancedPrivacySettings, arguments: _walletNewVM.type);
|
"type": _walletNewVM.type,
|
||||||
|
"useTestnet": _walletNewVM.useTestnet,
|
||||||
|
"toggleTestnet": _walletNewVM.toggleUseTestnet
|
||||||
|
});
|
||||||
},
|
},
|
||||||
child: Text(S.of(context).advanced_settings),
|
child: Text(S.of(context).advanced_settings),
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/anonpay_input_form.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/anonpay_input_form.dart';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
|
|
@ -210,8 +210,12 @@ class WalletRestorePage extends BasePage {
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pushNamed(Routes.advancedPrivacySettings,
|
Navigator.of(context)
|
||||||
arguments: walletRestoreViewModel.type);
|
.pushNamed(Routes.advancedPrivacySettings, arguments: {
|
||||||
|
'type': walletRestoreViewModel.type,
|
||||||
|
'useTestnet': walletRestoreViewModel.useTestnet,
|
||||||
|
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet
|
||||||
|
});
|
||||||
},
|
},
|
||||||
child: Text(S.of(context).advanced_settings),
|
child: Text(S.of(context).advanced_settings),
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:cake_wallet/anonpay/anonpay_request.dart';
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/currency.dart';
|
import 'package:cw_core/currency.dart';
|
||||||
|
|
|
@ -148,17 +148,21 @@ abstract class DashboardViewModelBase with Store {
|
||||||
monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
|
monero!.getTransactionInfoAccountId(tx) == monero!.getCurrentAccount(wallet).id)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
transactions = ObservableList.of(_accountTransactions.map((transaction) =>
|
final sortedTransactions = [..._accountTransactions];
|
||||||
TransactionListItem(
|
sortedTransactions.sort((a, b) => a.date.compareTo(b.date));
|
||||||
transaction: transaction,
|
|
||||||
balanceViewModel: balanceViewModel,
|
transactions = ObservableList.of(sortedTransactions.map((transaction) => TransactionListItem(
|
||||||
settingsStore: appStore.settingsStore)));
|
transaction: transaction,
|
||||||
|
balanceViewModel: balanceViewModel,
|
||||||
|
settingsStore: appStore.settingsStore)));
|
||||||
} else {
|
} else {
|
||||||
transactions = ObservableList.of(wallet.transactionHistory.transactions.values.map(
|
final sortedTransactions = [...wallet.transactionHistory.transactions.values];
|
||||||
(transaction) => TransactionListItem(
|
sortedTransactions.sort((a, b) => a.date.compareTo(b.date));
|
||||||
transaction: transaction,
|
|
||||||
balanceViewModel: balanceViewModel,
|
transactions = ObservableList.of(sortedTransactions.map((transaction) => TransactionListItem(
|
||||||
settingsStore: appStore.settingsStore)));
|
transaction: transaction,
|
||||||
|
balanceViewModel: balanceViewModel,
|
||||||
|
settingsStore: appStore.settingsStore)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: nano sub-account generation is disabled:
|
// TODO: nano sub-account generation is disabled:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
@ -9,11 +10,20 @@ class ReceiveOptionViewModel = ReceiveOptionViewModelBase with _$ReceiveOptionVi
|
||||||
|
|
||||||
abstract class ReceiveOptionViewModelBase with Store {
|
abstract class ReceiveOptionViewModelBase with Store {
|
||||||
ReceiveOptionViewModelBase(this._wallet, this.initialPageOption)
|
ReceiveOptionViewModelBase(this._wallet, this.initialPageOption)
|
||||||
: selectedReceiveOption = initialPageOption ?? ReceivePageOption.mainnet,
|
: selectedReceiveOption = initialPageOption ??
|
||||||
|
(_wallet.type == WalletType.bitcoin
|
||||||
|
? bitcoin!.getSelectedAddressType(_wallet)
|
||||||
|
: ReceivePageOption.mainnet),
|
||||||
_options = [] {
|
_options = [] {
|
||||||
final walletType = _wallet.type;
|
final walletType = _wallet.type;
|
||||||
_options =
|
_options = walletType == WalletType.haven
|
||||||
walletType == WalletType.haven ? [ReceivePageOption.mainnet] : ReceivePageOption.values;
|
? [ReceivePageOption.mainnet]
|
||||||
|
: walletType == WalletType.bitcoin
|
||||||
|
? [
|
||||||
|
...bitcoin!.getBitcoinReceivePageOptions(),
|
||||||
|
...ReceivePageOptions.where((element) => element != ReceivePageOption.mainnet)
|
||||||
|
]
|
||||||
|
: ReceivePageOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
final WalletBase _wallet;
|
final WalletBase _wallet;
|
||||||
|
|
|
@ -65,6 +65,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||||
bool get hasAuthCredentials =>
|
bool get hasAuthCredentials =>
|
||||||
_walletType == WalletType.monero || _walletType == WalletType.haven;
|
_walletType == WalletType.monero || _walletType == WalletType.haven;
|
||||||
|
|
||||||
|
bool get hasTestnetSupport => _walletType == WalletType.bitcoin;
|
||||||
|
|
||||||
String get uri {
|
String get uri {
|
||||||
var uri = address;
|
var uri = address;
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,11 @@ abstract class NodeListViewModelBase with Store {
|
||||||
|
|
||||||
switch (_appStore.wallet!.type) {
|
switch (_appStore.wallet!.type) {
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!;
|
if (_appStore.wallet!.isTestnet == true) {
|
||||||
|
node = getBitcoinTestnetDefaultElectrumServer(nodes: _nodeSource)!;
|
||||||
|
} else {
|
||||||
|
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource)!;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
node = getMoneroDefaultNode(nodes: _nodeSource);
|
node = getMoneroDefaultNode(nodes: _nodeSource);
|
||||||
|
|
|
@ -119,7 +119,7 @@ abstract class TransactionDetailsViewModelBase with Store {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
return 'https://monero.com/tx/${txId}';
|
return 'https://monero.com/tx/${txId}';
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
return 'https://mempool.space/tx/${txId}';
|
return 'https://mempool.space/${wallet.isTestnet == true ? "testnet/" : ""}tx/${txId}';
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return 'https://blockchair.com/litecoin/transaction/${txId}';
|
return 'https://blockchair.com/litecoin/transaction/${txId}';
|
||||||
case WalletType.bitcoinCash:
|
case WalletType.bitcoinCash:
|
||||||
|
|
|
@ -388,9 +388,6 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
wallet.type == WalletType.bitcoin ||
|
wallet.type == WalletType.bitcoin ||
|
||||||
wallet.type == WalletType.litecoin;
|
wallet.type == WalletType.litecoin;
|
||||||
|
|
||||||
// wallet.type == WalletType.nano ||
|
|
||||||
// wallet.type == WalletType.banano; TODO: nano accounts are disabled for now
|
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get isElectrumWallet =>
|
bool get isElectrumWallet =>
|
||||||
wallet.type == WalletType.bitcoin ||
|
wallet.type == WalletType.bitcoin ||
|
||||||
|
@ -409,16 +406,17 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||||
void setAddress(WalletAddressListItem address) =>
|
void setAddress(WalletAddressListItem address) =>
|
||||||
wallet.walletAddresses.address = address.address;
|
wallet.walletAddresses.address = address.address;
|
||||||
|
|
||||||
|
@action
|
||||||
|
Future<void> setAddressType(dynamic option) async {
|
||||||
|
if (wallet.type == WalletType.bitcoin) {
|
||||||
|
await bitcoin!.setAddressType(wallet, option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _init() {
|
void _init() {
|
||||||
_baseItems = [];
|
_baseItems = [];
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero ||
|
if (wallet.type == WalletType.monero || wallet.type == WalletType.haven) {
|
||||||
wallet.type ==
|
|
||||||
WalletType
|
|
||||||
.haven /*||
|
|
||||||
wallet.type == WalletType.nano ||
|
|
||||||
wallet.type == WalletType.banano*/
|
|
||||||
) {
|
|
||||||
_baseItems.add(WalletAccountListHeader());
|
_baseItems.add(WalletAccountListHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,12 @@ abstract class WalletCreationVMBase with Store {
|
||||||
: state = InitialExecutionState(),
|
: state = InitialExecutionState(),
|
||||||
name = '';
|
name = '';
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool _useTestnet = false;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
bool get useTestnet => _useTestnet;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
|
@ -94,4 +100,9 @@ abstract class WalletCreationVMBase with Store {
|
||||||
Future<WalletBase> processFromRestoredWallet(
|
Future<WalletBase> processFromRestoredWallet(
|
||||||
WalletCredentials credentials, RestoredWallet restoreWallet) =>
|
WalletCredentials credentials, RestoredWallet restoreWallet) =>
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
|
@action
|
||||||
|
void toggleUseTestnet(bool? value) {
|
||||||
|
_useTestnet = value ?? !_useTestnet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,6 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
|
||||||
@override
|
@override
|
||||||
Future<WalletBase> process(WalletCredentials credentials) async {
|
Future<WalletBase> process(WalletCredentials credentials) async {
|
||||||
walletCreationService.changeWalletType(type: type);
|
walletCreationService.changeWalletType(type: type);
|
||||||
return walletCreationService.create(credentials);
|
return walletCreationService.create(credentials, isTestnet: useTestnet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,9 +207,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
@override
|
@override
|
||||||
Future<WalletBase> process(WalletCredentials credentials) async {
|
Future<WalletBase> process(WalletCredentials credentials) async {
|
||||||
if (mode == WalletRestoreMode.keys) {
|
if (mode == WalletRestoreMode.keys) {
|
||||||
return walletCreationService.restoreFromKeys(credentials);
|
return walletCreationService.restoreFromKeys(credentials, isTestnet: useTestnet);
|
||||||
}
|
}
|
||||||
|
|
||||||
return walletCreationService.restoreFromSeed(credentials);
|
return walletCreationService.restoreFromSeed(credentials, isTestnet: useTestnet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "يتم تحويل الأموال إلى الدولار الأمريكي عند الاحتفاظ بها في الحساب المدفوع مسبقًا ، وليس بالعملات الرقمية.",
|
"use_card_info_two": "يتم تحويل الأموال إلى الدولار الأمريكي عند الاحتفاظ بها في الحساب المدفوع مسبقًا ، وليس بالعملات الرقمية.",
|
||||||
"use_ssl": "استخدم SSL",
|
"use_ssl": "استخدم SSL",
|
||||||
"use_suggested": "استخدام المقترح",
|
"use_suggested": "استخدام المقترح",
|
||||||
|
"use_testnet": "استخدم testnet",
|
||||||
"variable_pair_not_supported": "هذا الزوج المتغير غير مدعوم في التبادلات المحددة",
|
"variable_pair_not_supported": "هذا الزوج المتغير غير مدعوم في التبادلات المحددة",
|
||||||
"verification": "تَحَقّق",
|
"verification": "تَحَقّق",
|
||||||
"verify_with_2fa": "تحقق مع Cake 2FA",
|
"verify_with_2fa": "تحقق مع Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Средствата се обръщат в USD, когато биват запазени в предплатената карта, а не в дигитална валута.",
|
"use_card_info_two": "Средствата се обръщат в USD, когато биват запазени в предплатената карта, а не в дигитална валута.",
|
||||||
"use_ssl": "Използване на SSL",
|
"use_ssl": "Използване на SSL",
|
||||||
"use_suggested": "Използване на предложеното",
|
"use_suggested": "Използване на предложеното",
|
||||||
|
"use_testnet": "Използвайте TestNet",
|
||||||
"variable_pair_not_supported": "Този variable pair не се поддържа от избраната борса",
|
"variable_pair_not_supported": "Този variable pair не се поддържа от избраната борса",
|
||||||
"verification": "Потвърждаване",
|
"verification": "Потвърждаване",
|
||||||
"verify_with_2fa": "Проверете с Cake 2FA",
|
"verify_with_2fa": "Проверете с Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Prostředky jsou převedeny na USD, když jsou drženy na předplaceném účtu, nikoliv na digitální měnu.",
|
"use_card_info_two": "Prostředky jsou převedeny na USD, když jsou drženy na předplaceném účtu, nikoliv na digitální měnu.",
|
||||||
"use_ssl": "Použít SSL",
|
"use_ssl": "Použít SSL",
|
||||||
"use_suggested": "Použít doporučený",
|
"use_suggested": "Použít doporučený",
|
||||||
|
"use_testnet": "Použijte testNet",
|
||||||
"variable_pair_not_supported": "Tento pár s tržním kurzem není ve zvolené směnárně podporován",
|
"variable_pair_not_supported": "Tento pár s tržním kurzem není ve zvolené směnárně podporován",
|
||||||
"verification": "Ověření",
|
"verification": "Ověření",
|
||||||
"verify_with_2fa": "Ověřte pomocí Cake 2FA",
|
"verify_with_2fa": "Ověřte pomocí Cake 2FA",
|
||||||
|
|
|
@ -721,6 +721,7 @@
|
||||||
"use_card_info_two": "Guthaben werden auf dem Prepaid-Konto in USD umgerechnet, nicht in digitale Währung.",
|
"use_card_info_two": "Guthaben werden auf dem Prepaid-Konto in USD umgerechnet, nicht in digitale Währung.",
|
||||||
"use_ssl": "SSL verwenden",
|
"use_ssl": "SSL verwenden",
|
||||||
"use_suggested": "Vorgeschlagen verwenden",
|
"use_suggested": "Vorgeschlagen verwenden",
|
||||||
|
"use_testnet": "TESTNET verwenden",
|
||||||
"variable_pair_not_supported": "Dieses Variablenpaar wird von den ausgewählten Börsen nicht unterstützt",
|
"variable_pair_not_supported": "Dieses Variablenpaar wird von den ausgewählten Börsen nicht unterstützt",
|
||||||
"verification": "Verifizierung",
|
"verification": "Verifizierung",
|
||||||
"verify_with_2fa": "Verifizieren Sie mit Cake 2FA",
|
"verify_with_2fa": "Verifizieren Sie mit Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Funds are converted to USD when they're held in the prepaid account, not in digital currencies.",
|
"use_card_info_two": "Funds are converted to USD when they're held in the prepaid account, not in digital currencies.",
|
||||||
"use_ssl": "Use SSL",
|
"use_ssl": "Use SSL",
|
||||||
"use_suggested": "Use Suggested",
|
"use_suggested": "Use Suggested",
|
||||||
|
"use_testnet": "Use Testnet",
|
||||||
"variable_pair_not_supported": "This variable pair is not supported with the selected exchanges",
|
"variable_pair_not_supported": "This variable pair is not supported with the selected exchanges",
|
||||||
"verification": "Verification",
|
"verification": "Verification",
|
||||||
"verify_with_2fa": "Verify with Cake 2FA",
|
"verify_with_2fa": "Verify with Cake 2FA",
|
||||||
|
|
|
@ -720,6 +720,7 @@
|
||||||
"use_card_info_two": "Los fondos se convierten a USD cuando se mantienen en la cuenta prepaga, no en monedas digitales.",
|
"use_card_info_two": "Los fondos se convierten a USD cuando se mantienen en la cuenta prepaga, no en monedas digitales.",
|
||||||
"use_ssl": "Utilice SSL",
|
"use_ssl": "Utilice SSL",
|
||||||
"use_suggested": "Usar sugerido",
|
"use_suggested": "Usar sugerido",
|
||||||
|
"use_testnet": "Use TestNet",
|
||||||
"variable_pair_not_supported": "Este par de variables no es compatible con los intercambios seleccionados",
|
"variable_pair_not_supported": "Este par de variables no es compatible con los intercambios seleccionados",
|
||||||
"verification": "Verificación",
|
"verification": "Verificación",
|
||||||
"verify_with_2fa": "Verificar con Cake 2FA",
|
"verify_with_2fa": "Verificar con Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Les fonds sont convertis en USD lorsqu'ils sont détenus sur le compte prépayé, et non en devises numériques.",
|
"use_card_info_two": "Les fonds sont convertis en USD lorsqu'ils sont détenus sur le compte prépayé, et non en devises numériques.",
|
||||||
"use_ssl": "Utiliser SSL",
|
"use_ssl": "Utiliser SSL",
|
||||||
"use_suggested": "Suivre la suggestion",
|
"use_suggested": "Suivre la suggestion",
|
||||||
|
"use_testnet": "Utiliser TestNet",
|
||||||
"variable_pair_not_supported": "Cette paire variable n'est pas prise en charge avec les échanges sélectionnés",
|
"variable_pair_not_supported": "Cette paire variable n'est pas prise en charge avec les échanges sélectionnés",
|
||||||
"verification": "Vérification",
|
"verification": "Vérification",
|
||||||
"verify_with_2fa": "Vérifier avec Cake 2FA",
|
"verify_with_2fa": "Vérifier avec Cake 2FA",
|
||||||
|
|
|
@ -721,6 +721,7 @@
|
||||||
"use_card_info_two": "Ana canza kuɗi zuwa dalar Amurka lokacin da ake riƙe su a cikin asusun da aka riga aka biya, ba cikin agogon dijital ba.",
|
"use_card_info_two": "Ana canza kuɗi zuwa dalar Amurka lokacin da ake riƙe su a cikin asusun da aka riga aka biya, ba cikin agogon dijital ba.",
|
||||||
"use_ssl": "Yi amfani da SSL",
|
"use_ssl": "Yi amfani da SSL",
|
||||||
"use_suggested": "Amfani da Shawarwari",
|
"use_suggested": "Amfani da Shawarwari",
|
||||||
|
"use_testnet": "Amfani da gwaji",
|
||||||
"variable_pair_not_supported": "Ba a samun goyan bayan wannan m biyu tare da zaɓaɓɓun musayar",
|
"variable_pair_not_supported": "Ba a samun goyan bayan wannan m biyu tare da zaɓaɓɓun musayar",
|
||||||
"verification": "tabbatar",
|
"verification": "tabbatar",
|
||||||
"verify_with_2fa": "Tabbatar da Cake 2FA",
|
"verify_with_2fa": "Tabbatar da Cake 2FA",
|
||||||
|
|
|
@ -721,6 +721,7 @@
|
||||||
"use_card_info_two": "डिजिटल मुद्राओं में नहीं, प्रीपेड खाते में रखे जाने पर निधियों को यूएसडी में बदल दिया जाता है।",
|
"use_card_info_two": "डिजिटल मुद्राओं में नहीं, प्रीपेड खाते में रखे जाने पर निधियों को यूएसडी में बदल दिया जाता है।",
|
||||||
"use_ssl": "उपयोग SSL",
|
"use_ssl": "उपयोग SSL",
|
||||||
"use_suggested": "सुझाए गए का प्रयोग करें",
|
"use_suggested": "सुझाए गए का प्रयोग करें",
|
||||||
|
"use_testnet": "टेस्टनेट का उपयोग करें",
|
||||||
"variable_pair_not_supported": "यह परिवर्तनीय जोड़ी चयनित एक्सचेंजों के साथ समर्थित नहीं है",
|
"variable_pair_not_supported": "यह परिवर्तनीय जोड़ी चयनित एक्सचेंजों के साथ समर्थित नहीं है",
|
||||||
"verification": "सत्यापन",
|
"verification": "सत्यापन",
|
||||||
"verify_with_2fa": "केक 2FA के साथ सत्यापित करें",
|
"verify_with_2fa": "केक 2FA के साथ सत्यापित करें",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Sredstva se pretvaraju u USD kada se drže na prepaid računu, a ne u digitalnim valutama.",
|
"use_card_info_two": "Sredstva se pretvaraju u USD kada se drže na prepaid računu, a ne u digitalnim valutama.",
|
||||||
"use_ssl": "Koristi SSL",
|
"use_ssl": "Koristi SSL",
|
||||||
"use_suggested": "Koristite predloženo",
|
"use_suggested": "Koristite predloženo",
|
||||||
|
"use_testnet": "Koristite TestNet",
|
||||||
"variable_pair_not_supported": "Ovaj par varijabli nije podržan s odabranim burzama",
|
"variable_pair_not_supported": "Ovaj par varijabli nije podržan s odabranim burzama",
|
||||||
"verification": "Potvrda",
|
"verification": "Potvrda",
|
||||||
"verify_with_2fa": "Provjerite s Cake 2FA",
|
"verify_with_2fa": "Provjerite s Cake 2FA",
|
||||||
|
|
|
@ -722,6 +722,7 @@
|
||||||
"use_card_info_two": "Dana dikonversi ke USD ketika disimpan dalam akun pra-bayar, bukan dalam mata uang digital.",
|
"use_card_info_two": "Dana dikonversi ke USD ketika disimpan dalam akun pra-bayar, bukan dalam mata uang digital.",
|
||||||
"use_ssl": "Gunakan SSL",
|
"use_ssl": "Gunakan SSL",
|
||||||
"use_suggested": "Gunakan yang Disarankan",
|
"use_suggested": "Gunakan yang Disarankan",
|
||||||
|
"use_testnet": "Gunakan TestNet",
|
||||||
"variable_pair_not_supported": "Pasangan variabel ini tidak didukung dengan bursa yang dipilih",
|
"variable_pair_not_supported": "Pasangan variabel ini tidak didukung dengan bursa yang dipilih",
|
||||||
"verification": "Verifikasi",
|
"verification": "Verifikasi",
|
||||||
"verify_with_2fa": "Verifikasi dengan Cake 2FA",
|
"verify_with_2fa": "Verifikasi dengan Cake 2FA",
|
||||||
|
|
|
@ -721,6 +721,7 @@
|
||||||
"use_card_info_two": "I fondi vengono convertiti in USD quando sono detenuti nel conto prepagato, non in valute digitali.",
|
"use_card_info_two": "I fondi vengono convertiti in USD quando sono detenuti nel conto prepagato, non in valute digitali.",
|
||||||
"use_ssl": "Usa SSL",
|
"use_ssl": "Usa SSL",
|
||||||
"use_suggested": "Usa suggerito",
|
"use_suggested": "Usa suggerito",
|
||||||
|
"use_testnet": "Usa TestNet",
|
||||||
"variable_pair_not_supported": "Questa coppia di variabili non è supportata con gli scambi selezionati",
|
"variable_pair_not_supported": "Questa coppia di variabili non è supportata con gli scambi selezionati",
|
||||||
"verification": "Verifica",
|
"verification": "Verifica",
|
||||||
"verify_with_2fa": "Verifica con Cake 2FA",
|
"verify_with_2fa": "Verifica con Cake 2FA",
|
||||||
|
|
|
@ -720,6 +720,7 @@
|
||||||
"use_card_info_two": "デジタル通貨ではなく、プリペイドアカウントで保持されている場合、資金は米ドルに変換されます。",
|
"use_card_info_two": "デジタル通貨ではなく、プリペイドアカウントで保持されている場合、資金は米ドルに変換されます。",
|
||||||
"use_ssl": "SSLを使用する",
|
"use_ssl": "SSLを使用する",
|
||||||
"use_suggested": "推奨を使用",
|
"use_suggested": "推奨を使用",
|
||||||
|
"use_testnet": "テストネットを使用します",
|
||||||
"variable_pair_not_supported": "この変数ペアは、選択した取引所ではサポートされていません",
|
"variable_pair_not_supported": "この変数ペアは、選択した取引所ではサポートされていません",
|
||||||
"verification": "検証",
|
"verification": "検証",
|
||||||
"verify_with_2fa": "Cake 2FA で検証する",
|
"verify_with_2fa": "Cake 2FA で検証する",
|
||||||
|
|
|
@ -720,6 +720,7 @@
|
||||||
"use_card_info_two": "디지털 화폐가 아닌 선불 계정에 보유하면 자금이 USD로 변환됩니다.",
|
"use_card_info_two": "디지털 화폐가 아닌 선불 계정에 보유하면 자금이 USD로 변환됩니다.",
|
||||||
"use_ssl": "SSL 사용",
|
"use_ssl": "SSL 사용",
|
||||||
"use_suggested": "추천 사용",
|
"use_suggested": "추천 사용",
|
||||||
|
"use_testnet": "TestNet을 사용하십시오",
|
||||||
"variable_pair_not_supported": "이 변수 쌍은 선택한 교환에서 지원되지 않습니다.",
|
"variable_pair_not_supported": "이 변수 쌍은 선택한 교환에서 지원되지 않습니다.",
|
||||||
"verification": "검증",
|
"verification": "검증",
|
||||||
"verify_with_2fa": "케이크 2FA로 확인",
|
"verify_with_2fa": "케이크 2FA로 확인",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "ဒစ်ဂျစ်တယ်ငွေကြေးများဖြင့်မဟုတ်ဘဲ ကြိုတင်ငွေပေးချေသည့်အကောင့်တွင် သိမ်းထားသည့်အခါ ရန်ပုံငွေများကို USD သို့ ပြောင်းလဲပါသည်။",
|
"use_card_info_two": "ဒစ်ဂျစ်တယ်ငွေကြေးများဖြင့်မဟုတ်ဘဲ ကြိုတင်ငွေပေးချေသည့်အကောင့်တွင် သိမ်းထားသည့်အခါ ရန်ပုံငွေများကို USD သို့ ပြောင်းလဲပါသည်။",
|
||||||
"use_ssl": "SSL ကိုသုံးပါ။",
|
"use_ssl": "SSL ကိုသုံးပါ။",
|
||||||
"use_suggested": "အကြံပြုထားသည်ကို အသုံးပြုပါ။",
|
"use_suggested": "အကြံပြုထားသည်ကို အသုံးပြုပါ။",
|
||||||
|
"use_testnet": "testnet ကိုသုံးပါ",
|
||||||
"variable_pair_not_supported": "ရွေးချယ်ထားသော ဖလှယ်မှုများဖြင့် ဤပြောင်းလဲနိုင်သောအတွဲကို ပံ့ပိုးမထားပါ။",
|
"variable_pair_not_supported": "ရွေးချယ်ထားသော ဖလှယ်မှုများဖြင့် ဤပြောင်းလဲနိုင်သောအတွဲကို ပံ့ပိုးမထားပါ။",
|
||||||
"verification": "စိစစ်ခြင်း။",
|
"verification": "စိစစ်ခြင်း။",
|
||||||
"verify_with_2fa": "Cake 2FA ဖြင့် စစ်ဆေးပါ။",
|
"verify_with_2fa": "Cake 2FA ဖြင့် စစ်ဆေးပါ။",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Tegoeden worden omgezet naar USD wanneer ze op de prepaid-rekening staan, niet in digitale valuta.",
|
"use_card_info_two": "Tegoeden worden omgezet naar USD wanneer ze op de prepaid-rekening staan, niet in digitale valuta.",
|
||||||
"use_ssl": "Gebruik SSL",
|
"use_ssl": "Gebruik SSL",
|
||||||
"use_suggested": "Gebruik aanbevolen",
|
"use_suggested": "Gebruik aanbevolen",
|
||||||
|
"use_testnet": "Gebruik testnet",
|
||||||
"variable_pair_not_supported": "Dit variabelenpaar wordt niet ondersteund met de geselecteerde uitwisselingen",
|
"variable_pair_not_supported": "Dit variabelenpaar wordt niet ondersteund met de geselecteerde uitwisselingen",
|
||||||
"verification": "Verificatie",
|
"verification": "Verificatie",
|
||||||
"verify_with_2fa": "Controleer met Cake 2FA",
|
"verify_with_2fa": "Controleer met Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Środki są przeliczane na USD, gdy są przechowywane na koncie przedpłaconym, a nie w walutach cyfrowych.",
|
"use_card_info_two": "Środki są przeliczane na USD, gdy są przechowywane na koncie przedpłaconym, a nie w walutach cyfrowych.",
|
||||||
"use_ssl": "Użyj SSL",
|
"use_ssl": "Użyj SSL",
|
||||||
"use_suggested": "Użyj sugerowane",
|
"use_suggested": "Użyj sugerowane",
|
||||||
|
"use_testnet": "Użyj testne",
|
||||||
"variable_pair_not_supported": "Ta para zmiennych nie jest obsługiwana na wybranych giełdach",
|
"variable_pair_not_supported": "Ta para zmiennych nie jest obsługiwana na wybranych giełdach",
|
||||||
"verification": "Weryfikacja",
|
"verification": "Weryfikacja",
|
||||||
"verify_with_2fa": "Sprawdź za pomocą Cake 2FA",
|
"verify_with_2fa": "Sprawdź za pomocą Cake 2FA",
|
||||||
|
|
|
@ -721,6 +721,7 @@
|
||||||
"use_card_info_two": "Os fundos são convertidos para USD quando mantidos na conta pré-paga, não em moedas digitais.",
|
"use_card_info_two": "Os fundos são convertidos para USD quando mantidos na conta pré-paga, não em moedas digitais.",
|
||||||
"use_ssl": "Use SSL",
|
"use_ssl": "Use SSL",
|
||||||
"use_suggested": "Uso sugerido",
|
"use_suggested": "Uso sugerido",
|
||||||
|
"use_testnet": "Use testNet",
|
||||||
"variable_pair_not_supported": "Este par de variáveis não é compatível com as trocas selecionadas",
|
"variable_pair_not_supported": "Este par de variáveis não é compatível com as trocas selecionadas",
|
||||||
"verification": "Verificação",
|
"verification": "Verificação",
|
||||||
"verify_with_2fa": "Verificar com Cake 2FA",
|
"verify_with_2fa": "Verificar com Cake 2FA",
|
||||||
|
|
|
@ -720,6 +720,7 @@
|
||||||
"use_card_info_two": "Средства конвертируются в доллары США, когда они хранятся на предоплаченном счете, а не в цифровых валютах.",
|
"use_card_info_two": "Средства конвертируются в доллары США, когда они хранятся на предоплаченном счете, а не в цифровых валютах.",
|
||||||
"use_ssl": "Использовать SSL",
|
"use_ssl": "Использовать SSL",
|
||||||
"use_suggested": "Использовать предложенный",
|
"use_suggested": "Использовать предложенный",
|
||||||
|
"use_testnet": "Используйте Testnet",
|
||||||
"variable_pair_not_supported": "Эта пара переменных не поддерживается выбранными биржами.",
|
"variable_pair_not_supported": "Эта пара переменных не поддерживается выбранными биржами.",
|
||||||
"verification": "Проверка",
|
"verification": "Проверка",
|
||||||
"verify_with_2fa": "Подтвердить с помощью Cake 2FA",
|
"verify_with_2fa": "Подтвердить с помощью Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "เงินจะถูกแปลงค่าเป็นดอลลาร์สหรัฐเมื่อถือไว้ในบัญชีสำรองเงิน ไม่ใช่สกุลเงินดิจิตอล",
|
"use_card_info_two": "เงินจะถูกแปลงค่าเป็นดอลลาร์สหรัฐเมื่อถือไว้ในบัญชีสำรองเงิน ไม่ใช่สกุลเงินดิจิตอล",
|
||||||
"use_ssl": "ใช้ SSL",
|
"use_ssl": "ใช้ SSL",
|
||||||
"use_suggested": "ใช้ที่แนะนำ",
|
"use_suggested": "ใช้ที่แนะนำ",
|
||||||
|
"use_testnet": "ใช้ testnet",
|
||||||
"variable_pair_not_supported": "คู่ความสัมพันธ์ที่เปลี่ยนแปลงได้นี้ไม่สนับสนุนกับหุ้นที่เลือก",
|
"variable_pair_not_supported": "คู่ความสัมพันธ์ที่เปลี่ยนแปลงได้นี้ไม่สนับสนุนกับหุ้นที่เลือก",
|
||||||
"verification": "การตรวจสอบ",
|
"verification": "การตรวจสอบ",
|
||||||
"verify_with_2fa": "ตรวจสอบกับ Cake 2FA",
|
"verify_with_2fa": "ตรวจสอบกับ Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Ang mga pondo ay na -convert sa USD kapag gaganapin sila sa prepaid account, hindi sa mga digital na pera.",
|
"use_card_info_two": "Ang mga pondo ay na -convert sa USD kapag gaganapin sila sa prepaid account, hindi sa mga digital na pera.",
|
||||||
"use_ssl": "Gumamit ng SSL",
|
"use_ssl": "Gumamit ng SSL",
|
||||||
"use_suggested": "Gumamit ng iminungkahing",
|
"use_suggested": "Gumamit ng iminungkahing",
|
||||||
|
"use_testnet": "Gumamit ng testnet",
|
||||||
"variable_pair_not_supported": "Ang variable na pares na ito ay hindi suportado sa mga napiling palitan",
|
"variable_pair_not_supported": "Ang variable na pares na ito ay hindi suportado sa mga napiling palitan",
|
||||||
"verification": "Pag -verify",
|
"verification": "Pag -verify",
|
||||||
"verify_with_2fa": "Mag -verify sa cake 2FA",
|
"verify_with_2fa": "Mag -verify sa cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "Paralar, dijital para birimlerinde değil, ön ödemeli hesapta tutulduğunda USD'ye dönüştürülür.",
|
"use_card_info_two": "Paralar, dijital para birimlerinde değil, ön ödemeli hesapta tutulduğunda USD'ye dönüştürülür.",
|
||||||
"use_ssl": "SSL kullan",
|
"use_ssl": "SSL kullan",
|
||||||
"use_suggested": "Önerileni Kullan",
|
"use_suggested": "Önerileni Kullan",
|
||||||
|
"use_testnet": "TestNet kullanın",
|
||||||
"variable_pair_not_supported": "Bu değişken paritesi seçilen borsalarda desteklenmemekte",
|
"variable_pair_not_supported": "Bu değişken paritesi seçilen borsalarda desteklenmemekte",
|
||||||
"verification": "Doğrulama",
|
"verification": "Doğrulama",
|
||||||
"verify_with_2fa": "Cake 2FA ile Doğrulayın",
|
"verify_with_2fa": "Cake 2FA ile Doğrulayın",
|
||||||
|
|
|
@ -720,6 +720,7 @@
|
||||||
"use_card_info_two": "Кошти конвертуються в долари США, якщо вони зберігаються на передплаченому рахунку, а не в цифрових валютах.",
|
"use_card_info_two": "Кошти конвертуються в долари США, якщо вони зберігаються на передплаченому рахунку, а не в цифрових валютах.",
|
||||||
"use_ssl": "Використати SSL",
|
"use_ssl": "Використати SSL",
|
||||||
"use_suggested": "Використати запропоноване",
|
"use_suggested": "Використати запропоноване",
|
||||||
|
"use_testnet": "Використовуйте тестову мережу",
|
||||||
"variable_pair_not_supported": "Ця пара змінних не підтримується вибраними біржами",
|
"variable_pair_not_supported": "Ця пара змінних не підтримується вибраними біржами",
|
||||||
"verification": "Перевірка",
|
"verification": "Перевірка",
|
||||||
"verify_with_2fa": "Перевірте за допомогою Cake 2FA",
|
"verify_with_2fa": "Перевірте за допомогою Cake 2FA",
|
||||||
|
|
|
@ -721,6 +721,7 @@
|
||||||
"use_card_info_two": "رقوم کو امریکی ڈالر میں تبدیل کیا جاتا ہے جب پری پیڈ اکاؤنٹ میں رکھا جاتا ہے، ڈیجیٹل کرنسیوں میں نہیں۔",
|
"use_card_info_two": "رقوم کو امریکی ڈالر میں تبدیل کیا جاتا ہے جب پری پیڈ اکاؤنٹ میں رکھا جاتا ہے، ڈیجیٹل کرنسیوں میں نہیں۔",
|
||||||
"use_ssl": "SSL استعمال کریں۔",
|
"use_ssl": "SSL استعمال کریں۔",
|
||||||
"use_suggested": "تجویز کردہ استعمال کریں۔",
|
"use_suggested": "تجویز کردہ استعمال کریں۔",
|
||||||
|
"use_testnet": "ٹیسٹ نیٹ استعمال کریں",
|
||||||
"variable_pair_not_supported": "یہ متغیر جوڑا منتخب ایکسچینجز کے ساتھ تعاون یافتہ نہیں ہے۔",
|
"variable_pair_not_supported": "یہ متغیر جوڑا منتخب ایکسچینجز کے ساتھ تعاون یافتہ نہیں ہے۔",
|
||||||
"verification": "تصدیق",
|
"verification": "تصدیق",
|
||||||
"verify_with_2fa": "کیک 2FA سے تصدیق کریں۔",
|
"verify_with_2fa": "کیک 2FA سے تصدیق کریں۔",
|
||||||
|
|
|
@ -720,6 +720,7 @@
|
||||||
"use_card_info_two": "A pààrọ̀ owó sí owó Amẹ́ríkà tó bá wà nínú àkanti t'á ti fikún tẹ́lẹ̀tẹ́lẹ̀. A kò kó owó náà nínú owó ayélujára.",
|
"use_card_info_two": "A pààrọ̀ owó sí owó Amẹ́ríkà tó bá wà nínú àkanti t'á ti fikún tẹ́lẹ̀tẹ́lẹ̀. A kò kó owó náà nínú owó ayélujára.",
|
||||||
"use_ssl": "Lo SSL",
|
"use_ssl": "Lo SSL",
|
||||||
"use_suggested": "Lo àbá",
|
"use_suggested": "Lo àbá",
|
||||||
|
"use_testnet": "Lo tele",
|
||||||
"variable_pair_not_supported": "A kì í ṣe k'á fi àwọn ilé pàṣípààrọ̀ yìí ṣe pàṣípààrọ̀ irú owó méji yìí",
|
"variable_pair_not_supported": "A kì í ṣe k'á fi àwọn ilé pàṣípààrọ̀ yìí ṣe pàṣípààrọ̀ irú owó méji yìí",
|
||||||
"verification": "Ìjẹ́rìísí",
|
"verification": "Ìjẹ́rìísí",
|
||||||
"verify_with_2fa": "Ṣeẹda pẹlu Cake 2FA",
|
"verify_with_2fa": "Ṣeẹda pẹlu Cake 2FA",
|
||||||
|
|
|
@ -719,6 +719,7 @@
|
||||||
"use_card_info_two": "预付账户中的资金转换为美元,不是数字货币。",
|
"use_card_info_two": "预付账户中的资金转换为美元,不是数字货币。",
|
||||||
"use_ssl": "使用SSL",
|
"use_ssl": "使用SSL",
|
||||||
"use_suggested": "使用建议",
|
"use_suggested": "使用建议",
|
||||||
|
"use_testnet": "使用TestNet",
|
||||||
"variable_pair_not_supported": "所选交易所不支持此变量对",
|
"variable_pair_not_supported": "所选交易所不支持此变量对",
|
||||||
"verification": "验证",
|
"verification": "验证",
|
||||||
"verify_with_2fa": "用 Cake 2FA 验证",
|
"verify_with_2fa": "用 Cake 2FA 验证",
|
||||||
|
|
|
@ -22,8 +22,8 @@ MONERO_COM_PACKAGE="com.monero.app"
|
||||||
MONERO_COM_SCHEME="monero.com"
|
MONERO_COM_SCHEME="monero.com"
|
||||||
|
|
||||||
CAKEWALLET_NAME="Cake Wallet"
|
CAKEWALLET_NAME="Cake Wallet"
|
||||||
CAKEWALLET_VERSION="4.13.3"
|
CAKEWALLET_VERSION="4.14.0"
|
||||||
CAKEWALLET_BUILD_NUMBER=192
|
CAKEWALLET_BUILD_NUMBER=193
|
||||||
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
|
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
|
||||||
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
|
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
|
||||||
CAKEWALLET_SCHEME="cakewallet"
|
CAKEWALLET_SCHEME="cakewallet"
|
||||||
|
|
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
|
||||||
|
];
|
||||||
|
}
|
|
@ -18,8 +18,8 @@ MONERO_COM_BUILD_NUMBER=73
|
||||||
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
|
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
|
||||||
|
|
||||||
CAKEWALLET_NAME="Cake Wallet"
|
CAKEWALLET_NAME="Cake Wallet"
|
||||||
CAKEWALLET_VERSION="4.13.3"
|
CAKEWALLET_VERSION="4.14.0"
|
||||||
CAKEWALLET_BUILD_NUMBER=212
|
CAKEWALLET_BUILD_NUMBER=213
|
||||||
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
|
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
|
||||||
|
|
||||||
HAVEN_NAME="Haven"
|
HAVEN_NAME="Haven"
|
||||||
|
|
|
@ -72,6 +72,7 @@ import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:hive/hive.dart';""";
|
import 'package:hive/hive.dart';""";
|
||||||
const bitcoinCWHeaders = """
|
const bitcoinCWHeaders = """
|
||||||
|
import 'package:cw_bitcoin/bitcoin_receive_page_option.dart';
|
||||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
|
@ -82,6 +83,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||||
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
""";
|
""";
|
||||||
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
||||||
|
@ -139,6 +141,10 @@ abstract class Bitcoin {
|
||||||
TransactionPriority getLitecoinTransactionPriorityMedium();
|
TransactionPriority getLitecoinTransactionPriorityMedium();
|
||||||
TransactionPriority getBitcoinTransactionPrioritySlow();
|
TransactionPriority getBitcoinTransactionPrioritySlow();
|
||||||
TransactionPriority getLitecoinTransactionPrioritySlow();
|
TransactionPriority getLitecoinTransactionPrioritySlow();
|
||||||
|
|
||||||
|
Future<void> setAddressType(Object wallet, dynamic option);
|
||||||
|
BitcoinReceivePageOption getSelectedAddressType(Object wallet);
|
||||||
|
List<BitcoinReceivePageOption> getBitcoinReceivePageOptions();
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue