mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
Configure wallet types for the app
This commit is contained in:
parent
dd784a7829
commit
e6b1da376d
224 changed files with 903 additions and 23743 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -113,3 +113,6 @@ ios/build
|
|||
shared_external/**
|
||||
cw_shared_external/**
|
||||
cw_haven/**
|
||||
|
||||
lib/bitcoin/bitcoin.dart
|
||||
lib/monero/monero.dart
|
|
@ -2,10 +2,7 @@ analyzer:
|
|||
strong-mode:
|
||||
implicit-casts: false
|
||||
implicit-dynamic: false
|
||||
exclude:
|
||||
- "build/**"
|
||||
- "lib/generated/*.dart"
|
||||
- "**.g.dart"
|
||||
exclude: [build/**, lib/generated/*.dart, lib/**.g.dart, cw_monero/ios/External/**, cw_shared_external/**, shared_external/**]
|
||||
|
||||
linter:
|
||||
rules:
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:cw_monero/signatures.dart';
|
||||
import 'package:cw_monero/types.dart';
|
||||
import 'package:cw_monero/monero_api.dart';
|
||||
import 'package:cw_monero/structs/account_row.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_monero/wallet.dart';
|
||||
|
||||
final accountSizeNative = moneroApi
|
||||
.lookup<NativeFunction<account_size>>('account_size')
|
||||
.asFunction<SubaddressSize>();
|
||||
|
||||
final accountRefreshNative = moneroApi
|
||||
.lookup<NativeFunction<account_refresh>>('account_refresh')
|
||||
.asFunction<AccountRefresh>();
|
||||
|
||||
final accountGetAllNative = moneroApi
|
||||
.lookup<NativeFunction<account_get_all>>('account_get_all')
|
||||
.asFunction<AccountGetAll>();
|
||||
|
||||
final accountAddNewNative = moneroApi
|
||||
.lookup<NativeFunction<account_add_new>>('account_add_row')
|
||||
.asFunction<AccountAddNew>();
|
||||
|
||||
final accountSetLabelNative = moneroApi
|
||||
.lookup<NativeFunction<account_set_label>>('account_set_label_row')
|
||||
.asFunction<AccountSetLabel>();
|
||||
|
||||
bool isUpdating = false;
|
||||
|
||||
void refreshAccounts() {
|
||||
try {
|
||||
isUpdating = true;
|
||||
accountRefreshNative();
|
||||
isUpdating = false;
|
||||
} catch (e) {
|
||||
isUpdating = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
List<AccountRow> getAllAccount() {
|
||||
final size = accountSizeNative();
|
||||
final accountAddressesPointer = accountGetAllNative();
|
||||
final accountAddresses = accountAddressesPointer.asTypedList(size);
|
||||
|
||||
return accountAddresses
|
||||
.map((addr) => Pointer<AccountRow>.fromAddress(addr).ref)
|
||||
.toList();
|
||||
}
|
||||
|
||||
void addAccountSync({String label}) {
|
||||
final labelPointer = Utf8.toUtf8(label);
|
||||
accountAddNewNative(labelPointer);
|
||||
free(labelPointer);
|
||||
}
|
||||
|
||||
void setLabelForAccountSync({int accountIndex, String label}) {
|
||||
final labelPointer = Utf8.toUtf8(label);
|
||||
accountSetLabelNative(accountIndex, labelPointer);
|
||||
free(labelPointer);
|
||||
}
|
||||
|
||||
void _addAccount(String label) => addAccountSync(label: label);
|
||||
|
||||
void _setLabelForAccount(Map<String, dynamic> args) {
|
||||
final label = args['label'] as String;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
|
||||
setLabelForAccountSync(label: label, accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
Future<void> addAccount({String label}) async {
|
||||
await compute(_addAccount, label);
|
||||
await store();
|
||||
}
|
||||
|
||||
Future<void> setLabelForAccount({int accountIndex, String label}) async {
|
||||
await compute(
|
||||
_setLabelForAccount, {'accountIndex': accountIndex, 'label': label});
|
||||
await store();
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
String convertUTF8ToString({Pointer<Utf8> pointer}) {
|
||||
final str = Utf8.fromUtf8(pointer);
|
||||
free(pointer);
|
||||
return str;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
class ConnectionToNodeException implements Exception {
|
||||
ConnectionToNodeException({this.message});
|
||||
|
||||
final String message;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
class CreationTransactionException implements Exception {
|
||||
CreationTransactionException({this.message});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
class SetupWalletException implements Exception {
|
||||
SetupWalletException({this.message});
|
||||
|
||||
final String message;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
class WalletCreationException implements Exception {
|
||||
WalletCreationException({this.message});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
class WalletOpeningException implements Exception {
|
||||
WalletOpeningException({this.message});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
class WalletRestoreFromKeysException implements Exception {
|
||||
WalletRestoreFromKeysException({this.message});
|
||||
|
||||
final String message;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
class WalletRestoreFromSeedException implements Exception {
|
||||
WalletRestoreFromSeedException({this.message});
|
||||
|
||||
final String message;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
final DynamicLibrary moneroApi = Platform.isAndroid
|
||||
? DynamicLibrary.open("libcw_monero.so")
|
||||
: DynamicLibrary.open("cw_monero.framework/cw_monero");
|
|
@ -1,8 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class MoneroOutput {
|
||||
MoneroOutput({@required this.address, @required this.amount});
|
||||
|
||||
final String address;
|
||||
final String amount;
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:cw_monero/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/structs/ut8_box.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
typedef create_wallet = Int8 Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Pointer<Utf8>);
|
||||
|
||||
typedef restore_wallet_from_seed = Int8 Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||
|
||||
typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>,
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||
|
||||
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
|
||||
|
||||
typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
|
||||
|
||||
typedef error_string = Pointer<Utf8> Function();
|
||||
|
||||
typedef get_filename = Pointer<Utf8> Function();
|
||||
|
||||
typedef get_seed = Pointer<Utf8> Function();
|
||||
|
||||
typedef get_address = Pointer<Utf8> Function(Int32, Int32);
|
||||
|
||||
typedef get_full_balanace = Int64 Function(Int32);
|
||||
|
||||
typedef get_unlocked_balanace = Int64 Function(Int32);
|
||||
|
||||
typedef get_current_height = Int64 Function();
|
||||
|
||||
typedef get_node_height = Int64 Function();
|
||||
|
||||
typedef is_connected = Int8 Function();
|
||||
|
||||
typedef setup_node = Int8 Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int8, Int8, Pointer<Utf8>);
|
||||
|
||||
typedef start_refresh = Void Function();
|
||||
|
||||
typedef connect_to_node = Int8 Function();
|
||||
|
||||
typedef set_refresh_from_block_height = Void Function(Int64);
|
||||
|
||||
typedef set_recovering_from_seed = Void Function(Int8);
|
||||
|
||||
typedef store_c = Void Function(Pointer<Utf8>);
|
||||
|
||||
typedef set_listener = Void Function();
|
||||
|
||||
typedef get_syncing_height = Int64 Function();
|
||||
|
||||
typedef is_needed_to_refresh = Int8 Function();
|
||||
|
||||
typedef is_new_transaction_exist = Int8 Function();
|
||||
|
||||
typedef subaddrress_size = Int32 Function();
|
||||
|
||||
typedef subaddrress_refresh = Void Function(Int32);
|
||||
|
||||
typedef subaddress_get_all = Pointer<Int64> Function();
|
||||
|
||||
typedef subaddress_add_new = Void Function(
|
||||
Int32 accountIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef subaddress_set_label = Void Function(
|
||||
Int32 accountIndex, Int32 addressIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef account_size = Int32 Function();
|
||||
|
||||
typedef account_refresh = Void Function();
|
||||
|
||||
typedef account_get_all = Pointer<Int64> Function();
|
||||
|
||||
typedef account_add_new = Void Function(Pointer<Utf8> label);
|
||||
|
||||
typedef account_set_label = Void Function(
|
||||
Int32 accountIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef transactions_refresh = Void Function();
|
||||
|
||||
typedef get_tx_key = Pointer<Utf8> Function(Pointer<Utf8> txId);
|
||||
|
||||
typedef transactions_count = Int64 Function();
|
||||
|
||||
typedef transactions_get_all = Pointer<Int64> Function();
|
||||
|
||||
typedef transaction_create = Int8 Function(
|
||||
Pointer<Utf8> address,
|
||||
Pointer<Utf8> paymentId,
|
||||
Pointer<Utf8> amount,
|
||||
Int8 priorityRaw,
|
||||
Int32 subaddrAccount,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
typedef transaction_create_mult_dest = Int8 Function(
|
||||
Pointer<Pointer<Utf8>> addresses,
|
||||
Pointer<Utf8> paymentId,
|
||||
Pointer<Pointer<Utf8>> amounts,
|
||||
Int32 size,
|
||||
Int8 priorityRaw,
|
||||
Int32 subaddrAccount,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
typedef transaction_commit = Int8 Function(Pointer<PendingTransactionRaw>, Pointer<Utf8Box>);
|
||||
|
||||
typedef secret_view_key = Pointer<Utf8> Function();
|
||||
|
||||
typedef public_view_key = Pointer<Utf8> Function();
|
||||
|
||||
typedef secret_spend_key = Pointer<Utf8> Function();
|
||||
|
||||
typedef public_spend_key = Pointer<Utf8> Function();
|
||||
|
||||
typedef close_current_wallet = Void Function();
|
||||
|
||||
typedef on_startup = Void Function();
|
||||
|
||||
typedef rescan_blockchain = Void Function();
|
|
@ -1,11 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
class AccountRow extends Struct {
|
||||
@Int64()
|
||||
int id;
|
||||
Pointer<Utf8> label;
|
||||
|
||||
String getLabel() => Utf8.fromUtf8(label);
|
||||
int getId() => id;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
class PendingTransactionRaw extends Struct {
|
||||
@Int64()
|
||||
int amount;
|
||||
|
||||
@Int64()
|
||||
int fee;
|
||||
|
||||
Pointer<Utf8> hash;
|
||||
|
||||
String getHash() => Utf8.fromUtf8(hash);
|
||||
}
|
||||
|
||||
class PendingTransactionDescription {
|
||||
PendingTransactionDescription({this.amount, this.fee, this.hash, this.pointerAddress});
|
||||
|
||||
final int amount;
|
||||
final int fee;
|
||||
final String hash;
|
||||
final int pointerAddress;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
class SubaddressRow extends Struct {
|
||||
@Int64()
|
||||
int id;
|
||||
Pointer<Utf8> address;
|
||||
Pointer<Utf8> label;
|
||||
|
||||
String getLabel() => Utf8.fromUtf8(label);
|
||||
String getAddress() => Utf8.fromUtf8(address);
|
||||
int getId() => id;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
class TransactionInfoRow extends Struct {
|
||||
@Uint64()
|
||||
int amount;
|
||||
|
||||
@Uint64()
|
||||
int fee;
|
||||
|
||||
@Uint64()
|
||||
int blockHeight;
|
||||
|
||||
@Uint64()
|
||||
int confirmations;
|
||||
|
||||
@Uint32()
|
||||
int subaddrAccount;
|
||||
|
||||
@Int8()
|
||||
int direction;
|
||||
|
||||
@Int8()
|
||||
int isPending;
|
||||
|
||||
@Uint32()
|
||||
int subaddrIndex;
|
||||
|
||||
Pointer<Utf8> hash;
|
||||
|
||||
Pointer<Utf8> paymentId;
|
||||
|
||||
@Int64()
|
||||
int datetime;
|
||||
|
||||
int getDatetime() => datetime;
|
||||
int getAmount() => amount >= 0 ? amount : amount * -1;
|
||||
bool getIsPending() => isPending != 0;
|
||||
String getHash() => Utf8.fromUtf8(hash);
|
||||
String getPaymentId() => Utf8.fromUtf8(paymentId);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
class Utf8Box extends Struct {
|
||||
Pointer<Utf8> value;
|
||||
|
||||
String getValue() => Utf8.fromUtf8(value);
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_monero/signatures.dart';
|
||||
import 'package:cw_monero/types.dart';
|
||||
import 'package:cw_monero/monero_api.dart';
|
||||
import 'package:cw_monero/structs/subaddress_row.dart';
|
||||
import 'package:cw_monero/wallet.dart';
|
||||
|
||||
final subaddressSizeNative = moneroApi
|
||||
.lookup<NativeFunction<subaddrress_size>>('subaddrress_size')
|
||||
.asFunction<SubaddressSize>();
|
||||
|
||||
final subaddressRefreshNative = moneroApi
|
||||
.lookup<NativeFunction<subaddrress_refresh>>('subaddress_refresh')
|
||||
.asFunction<SubaddressRefresh>();
|
||||
|
||||
final subaddrressGetAllNative = moneroApi
|
||||
.lookup<NativeFunction<subaddress_get_all>>('subaddrress_get_all')
|
||||
.asFunction<SubaddressGetAll>();
|
||||
|
||||
final subaddrressAddNewNative = moneroApi
|
||||
.lookup<NativeFunction<subaddress_add_new>>('subaddress_add_row')
|
||||
.asFunction<SubaddressAddNew>();
|
||||
|
||||
final subaddrressSetLabelNative = moneroApi
|
||||
.lookup<NativeFunction<subaddress_set_label>>('subaddress_set_label')
|
||||
.asFunction<SubaddressSetLabel>();
|
||||
|
||||
bool isUpdating = false;
|
||||
|
||||
void refreshSubaddresses({@required int accountIndex}) {
|
||||
try {
|
||||
isUpdating = true;
|
||||
subaddressRefreshNative(accountIndex);
|
||||
isUpdating = false;
|
||||
} catch (e) {
|
||||
isUpdating = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
List<SubaddressRow> getAllSubaddresses() {
|
||||
final size = subaddressSizeNative();
|
||||
final subaddressAddressesPointer = subaddrressGetAllNative();
|
||||
final subaddressAddresses = subaddressAddressesPointer.asTypedList(size);
|
||||
|
||||
return subaddressAddresses
|
||||
.map((addr) => Pointer<SubaddressRow>.fromAddress(addr).ref)
|
||||
.toList();
|
||||
}
|
||||
|
||||
void addSubaddressSync({int accountIndex, String label}) {
|
||||
final labelPointer = Utf8.toUtf8(label);
|
||||
subaddrressAddNewNative(accountIndex, labelPointer);
|
||||
free(labelPointer);
|
||||
}
|
||||
|
||||
void setLabelForSubaddressSync(
|
||||
{int accountIndex, int addressIndex, String label}) {
|
||||
final labelPointer = Utf8.toUtf8(label);
|
||||
|
||||
subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer);
|
||||
free(labelPointer);
|
||||
}
|
||||
|
||||
void _addSubaddress(Map<String, dynamic> args) {
|
||||
final label = args['label'] as String;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
|
||||
addSubaddressSync(accountIndex: accountIndex, label: label);
|
||||
}
|
||||
|
||||
void _setLabelForSubaddress(Map<String, dynamic> args) {
|
||||
final label = args['label'] as String;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
final addressIndex = args['addressIndex'] as int;
|
||||
|
||||
setLabelForSubaddressSync(
|
||||
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
|
||||
}
|
||||
|
||||
Future addSubaddress({int accountIndex, String label}) async {
|
||||
await compute<Map<String, Object>, void>(
|
||||
_addSubaddress, {'accountIndex': accountIndex, 'label': label});
|
||||
await store();
|
||||
}
|
||||
|
||||
Future setLabelForSubaddress(
|
||||
{int accountIndex, int addressIndex, String label}) async {
|
||||
await compute<Map<String, Object>, void>(_setLabelForSubaddress, {
|
||||
'accountIndex': accountIndex,
|
||||
'addressIndex': addressIndex,
|
||||
'label': label
|
||||
});
|
||||
await store();
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:cw_monero/convert_utf8_to_string.dart';
|
||||
import 'package:cw_monero/monero_output.dart';
|
||||
import 'package:cw_monero/structs/ut8_box.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_monero/signatures.dart';
|
||||
import 'package:cw_monero/types.dart';
|
||||
import 'package:cw_monero/monero_api.dart';
|
||||
import 'package:cw_monero/structs/transaction_info_row.dart';
|
||||
import 'package:cw_monero/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/exceptions/creation_transaction_exception.dart';
|
||||
|
||||
final transactionsRefreshNative = moneroApi
|
||||
.lookup<NativeFunction<transactions_refresh>>('transactions_refresh')
|
||||
.asFunction<TransactionsRefresh>();
|
||||
|
||||
final transactionsCountNative = moneroApi
|
||||
.lookup<NativeFunction<transactions_count>>('transactions_count')
|
||||
.asFunction<TransactionsCount>();
|
||||
|
||||
final transactionsGetAllNative = moneroApi
|
||||
.lookup<NativeFunction<transactions_get_all>>('transactions_get_all')
|
||||
.asFunction<TransactionsGetAll>();
|
||||
|
||||
final transactionCreateNative = moneroApi
|
||||
.lookup<NativeFunction<transaction_create>>('transaction_create')
|
||||
.asFunction<TransactionCreate>();
|
||||
|
||||
final transactionCreateMultDestNative = moneroApi
|
||||
.lookup<NativeFunction<transaction_create_mult_dest>>('transaction_create_mult_dest')
|
||||
.asFunction<TransactionCreateMultDest>();
|
||||
|
||||
final transactionCommitNative = moneroApi
|
||||
.lookup<NativeFunction<transaction_commit>>('transaction_commit')
|
||||
.asFunction<TransactionCommit>();
|
||||
|
||||
final getTxKeyNative = moneroApi
|
||||
.lookup<NativeFunction<get_tx_key>>('get_tx_key')
|
||||
.asFunction<GetTxKey>();
|
||||
|
||||
String getTxKey(String txId) {
|
||||
final txIdPointer = Utf8.toUtf8(txId);
|
||||
final keyPointer = getTxKeyNative(txIdPointer);
|
||||
|
||||
free(txIdPointer);
|
||||
|
||||
if (keyPointer != null) {
|
||||
return convertUTF8ToString(pointer: keyPointer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void refreshTransactions() => transactionsRefreshNative();
|
||||
|
||||
int countOfTransactions() => transactionsCountNative();
|
||||
|
||||
List<TransactionInfoRow> getAllTransations() {
|
||||
final size = transactionsCountNative();
|
||||
final transactionsPointer = transactionsGetAllNative();
|
||||
final transactionsAddresses = transactionsPointer.asTypedList(size);
|
||||
|
||||
return transactionsAddresses
|
||||
.map((addr) => Pointer<TransactionInfoRow>.fromAddress(addr).ref)
|
||||
.toList();
|
||||
}
|
||||
|
||||
PendingTransactionDescription createTransactionSync(
|
||||
{String address,
|
||||
String paymentId,
|
||||
String amount,
|
||||
int priorityRaw,
|
||||
int accountIndex = 0}) {
|
||||
final addressPointer = Utf8.toUtf8(address);
|
||||
final paymentIdPointer = Utf8.toUtf8(paymentId);
|
||||
final amountPointer = amount != null ? Utf8.toUtf8(amount) : nullptr;
|
||||
final errorMessagePointer = allocate<Utf8Box>();
|
||||
final pendingTransactionRawPointer = allocate<PendingTransactionRaw>();
|
||||
final created = transactionCreateNative(
|
||||
addressPointer,
|
||||
paymentIdPointer,
|
||||
amountPointer,
|
||||
priorityRaw,
|
||||
accountIndex,
|
||||
errorMessagePointer,
|
||||
pendingTransactionRawPointer) !=
|
||||
0;
|
||||
|
||||
free(addressPointer);
|
||||
free(paymentIdPointer);
|
||||
|
||||
if (amountPointer != nullptr) {
|
||||
free(amountPointer);
|
||||
}
|
||||
|
||||
if (!created) {
|
||||
final message = errorMessagePointer.ref.getValue();
|
||||
free(errorMessagePointer);
|
||||
throw CreationTransactionException(message: message);
|
||||
}
|
||||
|
||||
return PendingTransactionDescription(
|
||||
amount: pendingTransactionRawPointer.ref.amount,
|
||||
fee: pendingTransactionRawPointer.ref.fee,
|
||||
hash: pendingTransactionRawPointer.ref.getHash(),
|
||||
pointerAddress: pendingTransactionRawPointer.address);
|
||||
}
|
||||
|
||||
PendingTransactionDescription createTransactionMultDestSync(
|
||||
{List<MoneroOutput> outputs,
|
||||
String paymentId,
|
||||
int priorityRaw,
|
||||
int accountIndex = 0}) {
|
||||
final int size = outputs.length;
|
||||
final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
|
||||
Utf8.toUtf8(output.address)).toList();
|
||||
final Pointer<Pointer<Utf8>> addressesPointerPointer = allocate(count: size);
|
||||
final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
|
||||
Utf8.toUtf8(output.amount)).toList();
|
||||
final Pointer<Pointer<Utf8>> amountsPointerPointer = allocate(count: size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
addressesPointerPointer[i] = addressesPointers[i];
|
||||
amountsPointerPointer[i] = amountsPointers[i];
|
||||
}
|
||||
|
||||
final paymentIdPointer = Utf8.toUtf8(paymentId);
|
||||
final errorMessagePointer = allocate<Utf8Box>();
|
||||
final pendingTransactionRawPointer = allocate<PendingTransactionRaw>();
|
||||
final created = transactionCreateMultDestNative(
|
||||
addressesPointerPointer,
|
||||
paymentIdPointer,
|
||||
amountsPointerPointer,
|
||||
size,
|
||||
priorityRaw,
|
||||
accountIndex,
|
||||
errorMessagePointer,
|
||||
pendingTransactionRawPointer) !=
|
||||
0;
|
||||
|
||||
free(addressesPointerPointer);
|
||||
free(amountsPointerPointer);
|
||||
|
||||
addressesPointers.forEach((element) => free(element));
|
||||
amountsPointers.forEach((element) => free(element));
|
||||
|
||||
free(paymentIdPointer);
|
||||
|
||||
if (!created) {
|
||||
final message = errorMessagePointer.ref.getValue();
|
||||
free(errorMessagePointer);
|
||||
throw CreationTransactionException(message: message);
|
||||
}
|
||||
|
||||
return PendingTransactionDescription(
|
||||
amount: pendingTransactionRawPointer.ref.amount,
|
||||
fee: pendingTransactionRawPointer.ref.fee,
|
||||
hash: pendingTransactionRawPointer.ref.getHash(),
|
||||
pointerAddress: pendingTransactionRawPointer.address);
|
||||
}
|
||||
|
||||
void commitTransactionFromPointerAddress({int address}) => commitTransaction(
|
||||
transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
|
||||
|
||||
void commitTransaction({Pointer<PendingTransactionRaw> transactionPointer}) {
|
||||
final errorMessagePointer = allocate<Utf8Box>();
|
||||
final isCommited =
|
||||
transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
|
||||
|
||||
if (!isCommited) {
|
||||
final message = errorMessagePointer.ref.getValue();
|
||||
free(errorMessagePointer);
|
||||
throw CreationTransactionException(message: message);
|
||||
}
|
||||
}
|
||||
|
||||
PendingTransactionDescription _createTransactionSync(Map args) {
|
||||
final address = args['address'] as String;
|
||||
final paymentId = args['paymentId'] as String;
|
||||
final amount = args['amount'] as String;
|
||||
final priorityRaw = args['priorityRaw'] as int;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
|
||||
return createTransactionSync(
|
||||
address: address,
|
||||
paymentId: paymentId,
|
||||
amount: amount,
|
||||
priorityRaw: priorityRaw,
|
||||
accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
PendingTransactionDescription _createTransactionMultDestSync(Map args) {
|
||||
final outputs = args['outputs'] as List<MoneroOutput>;
|
||||
final paymentId = args['paymentId'] as String;
|
||||
final priorityRaw = args['priorityRaw'] as int;
|
||||
final accountIndex = args['accountIndex'] as int;
|
||||
|
||||
return createTransactionMultDestSync(
|
||||
outputs: outputs,
|
||||
paymentId: paymentId,
|
||||
priorityRaw: priorityRaw,
|
||||
accountIndex: accountIndex);
|
||||
}
|
||||
|
||||
Future<PendingTransactionDescription> createTransaction(
|
||||
{String address,
|
||||
String paymentId = '',
|
||||
String amount,
|
||||
int priorityRaw,
|
||||
int accountIndex = 0}) =>
|
||||
compute(_createTransactionSync, {
|
||||
'address': address,
|
||||
'paymentId': paymentId,
|
||||
'amount': amount,
|
||||
'priorityRaw': priorityRaw,
|
||||
'accountIndex': accountIndex
|
||||
});
|
||||
|
||||
Future<PendingTransactionDescription> createTransactionMultDest(
|
||||
{List<MoneroOutput> outputs,
|
||||
String paymentId = '',
|
||||
int priorityRaw,
|
||||
int accountIndex = 0}) =>
|
||||
compute(_createTransactionMultDestSync, {
|
||||
'outputs': outputs,
|
||||
'paymentId': paymentId,
|
||||
'priorityRaw': priorityRaw,
|
||||
'accountIndex': accountIndex
|
||||
});
|
|
@ -1,120 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:cw_monero/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/structs/ut8_box.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
typedef CreateWallet = int Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, Pointer<Utf8>);
|
||||
|
||||
typedef RestoreWalletFromSeed = int Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
|
||||
|
||||
typedef RestoreWalletFromKeys = int Function(Pointer<Utf8>, Pointer<Utf8>,
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
|
||||
|
||||
typedef IsWalletExist = int Function(Pointer<Utf8>);
|
||||
|
||||
typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
|
||||
|
||||
typedef ErrorString = Pointer<Utf8> Function();
|
||||
|
||||
typedef GetFilename = Pointer<Utf8> Function();
|
||||
|
||||
typedef GetSeed = Pointer<Utf8> Function();
|
||||
|
||||
typedef GetAddress = Pointer<Utf8> Function(int, int);
|
||||
|
||||
typedef GetFullBalance = int Function(int);
|
||||
|
||||
typedef GetUnlockedBalance = int Function(int);
|
||||
|
||||
typedef GetCurrentHeight = int Function();
|
||||
|
||||
typedef GetNodeHeight = int Function();
|
||||
|
||||
typedef IsConnected = int Function();
|
||||
|
||||
typedef SetupNode = int Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
|
||||
|
||||
typedef StartRefresh = void Function();
|
||||
|
||||
typedef ConnectToNode = int Function();
|
||||
|
||||
typedef SetRefreshFromBlockHeight = void Function(int);
|
||||
|
||||
typedef SetRecoveringFromSeed = void Function(int);
|
||||
|
||||
typedef Store = void Function(Pointer<Utf8>);
|
||||
|
||||
typedef SetListener = void Function();
|
||||
|
||||
typedef GetSyncingHeight = int Function();
|
||||
|
||||
typedef IsNeededToRefresh = int Function();
|
||||
|
||||
typedef IsNewTransactionExist = int Function();
|
||||
|
||||
typedef SubaddressSize = int Function();
|
||||
|
||||
typedef SubaddressRefresh = void Function(int);
|
||||
|
||||
typedef SubaddressGetAll = Pointer<Int64> Function();
|
||||
|
||||
typedef SubaddressAddNew = void Function(int accountIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef SubaddressSetLabel = void Function(
|
||||
int accountIndex, int addressIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef AccountSize = int Function();
|
||||
|
||||
typedef AccountRefresh = void Function();
|
||||
|
||||
typedef AccountGetAll = Pointer<Int64> Function();
|
||||
|
||||
typedef AccountAddNew = void Function(Pointer<Utf8> label);
|
||||
|
||||
typedef AccountSetLabel = void Function(int accountIndex, Pointer<Utf8> label);
|
||||
|
||||
typedef TransactionsRefresh = void Function();
|
||||
|
||||
typedef GetTxKey = Pointer<Utf8> Function(Pointer<Utf8> txId);
|
||||
|
||||
typedef TransactionsCount = int Function();
|
||||
|
||||
typedef TransactionsGetAll = Pointer<Int64> Function();
|
||||
|
||||
typedef TransactionCreate = int Function(
|
||||
Pointer<Utf8> address,
|
||||
Pointer<Utf8> paymentId,
|
||||
Pointer<Utf8> amount,
|
||||
int priorityRaw,
|
||||
int subaddrAccount,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
typedef TransactionCreateMultDest = int Function(
|
||||
Pointer<Pointer<Utf8>> addresses,
|
||||
Pointer<Utf8> paymentId,
|
||||
Pointer<Pointer<Utf8>> amounts,
|
||||
int size,
|
||||
int priorityRaw,
|
||||
int subaddrAccount,
|
||||
Pointer<Utf8Box> error,
|
||||
Pointer<PendingTransactionRaw> pendingTransaction);
|
||||
|
||||
typedef TransactionCommit = int Function(Pointer<PendingTransactionRaw>, Pointer<Utf8Box>);
|
||||
|
||||
typedef SecretViewKey = Pointer<Utf8> Function();
|
||||
|
||||
typedef PublicViewKey = Pointer<Utf8> Function();
|
||||
|
||||
typedef SecretSpendKey = Pointer<Utf8> Function();
|
||||
|
||||
typedef PublicSpendKey = Pointer<Utf8> Function();
|
||||
|
||||
typedef CloseCurrentWallet = void Function();
|
||||
|
||||
typedef OnStartup = void Function();
|
||||
|
||||
typedef RescanBlockchainAsync = void Function();
|
|
@ -1,329 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:cw_monero/convert_utf8_to_string.dart';
|
||||
import 'package:cw_monero/signatures.dart';
|
||||
import 'package:cw_monero/types.dart';
|
||||
import 'package:cw_monero/monero_api.dart';
|
||||
import 'package:cw_monero/exceptions/setup_wallet_exception.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
int _boolToInt(bool value) => value ? 1 : 0;
|
||||
|
||||
final getFileNameNative = moneroApi
|
||||
.lookup<NativeFunction<get_filename>>('get_filename')
|
||||
.asFunction<GetFilename>();
|
||||
|
||||
final getSeedNative =
|
||||
moneroApi.lookup<NativeFunction<get_seed>>('seed').asFunction<GetSeed>();
|
||||
|
||||
final getAddressNative = moneroApi
|
||||
.lookup<NativeFunction<get_address>>('get_address')
|
||||
.asFunction<GetAddress>();
|
||||
|
||||
final getFullBalanceNative = moneroApi
|
||||
.lookup<NativeFunction<get_full_balanace>>('get_full_balance')
|
||||
.asFunction<GetFullBalance>();
|
||||
|
||||
final getUnlockedBalanceNative = moneroApi
|
||||
.lookup<NativeFunction<get_unlocked_balanace>>('get_unlocked_balance')
|
||||
.asFunction<GetUnlockedBalance>();
|
||||
|
||||
final getCurrentHeightNative = moneroApi
|
||||
.lookup<NativeFunction<get_current_height>>('get_current_height')
|
||||
.asFunction<GetCurrentHeight>();
|
||||
|
||||
final getNodeHeightNative = moneroApi
|
||||
.lookup<NativeFunction<get_node_height>>('get_node_height')
|
||||
.asFunction<GetNodeHeight>();
|
||||
|
||||
final isConnectedNative = moneroApi
|
||||
.lookup<NativeFunction<is_connected>>('is_connected')
|
||||
.asFunction<IsConnected>();
|
||||
|
||||
final setupNodeNative = moneroApi
|
||||
.lookup<NativeFunction<setup_node>>('setup_node')
|
||||
.asFunction<SetupNode>();
|
||||
|
||||
final startRefreshNative = moneroApi
|
||||
.lookup<NativeFunction<start_refresh>>('start_refresh')
|
||||
.asFunction<StartRefresh>();
|
||||
|
||||
final connecToNodeNative = moneroApi
|
||||
.lookup<NativeFunction<connect_to_node>>('connect_to_node')
|
||||
.asFunction<ConnectToNode>();
|
||||
|
||||
final setRefreshFromBlockHeightNative = moneroApi
|
||||
.lookup<NativeFunction<set_refresh_from_block_height>>(
|
||||
'set_refresh_from_block_height')
|
||||
.asFunction<SetRefreshFromBlockHeight>();
|
||||
|
||||
final setRecoveringFromSeedNative = moneroApi
|
||||
.lookup<NativeFunction<set_recovering_from_seed>>(
|
||||
'set_recovering_from_seed')
|
||||
.asFunction<SetRecoveringFromSeed>();
|
||||
|
||||
final storeNative =
|
||||
moneroApi.lookup<NativeFunction<store_c>>('store').asFunction<Store>();
|
||||
|
||||
final setListenerNative = moneroApi
|
||||
.lookup<NativeFunction<set_listener>>('set_listener')
|
||||
.asFunction<SetListener>();
|
||||
|
||||
final getSyncingHeightNative = moneroApi
|
||||
.lookup<NativeFunction<get_syncing_height>>('get_syncing_height')
|
||||
.asFunction<GetSyncingHeight>();
|
||||
|
||||
final isNeededToRefreshNative = moneroApi
|
||||
.lookup<NativeFunction<is_needed_to_refresh>>('is_needed_to_refresh')
|
||||
.asFunction<IsNeededToRefresh>();
|
||||
|
||||
final isNewTransactionExistNative = moneroApi
|
||||
.lookup<NativeFunction<is_new_transaction_exist>>(
|
||||
'is_new_transaction_exist')
|
||||
.asFunction<IsNewTransactionExist>();
|
||||
|
||||
final getSecretViewKeyNative = moneroApi
|
||||
.lookup<NativeFunction<secret_view_key>>('secret_view_key')
|
||||
.asFunction<SecretViewKey>();
|
||||
|
||||
final getPublicViewKeyNative = moneroApi
|
||||
.lookup<NativeFunction<public_view_key>>('public_view_key')
|
||||
.asFunction<PublicViewKey>();
|
||||
|
||||
final getSecretSpendKeyNative = moneroApi
|
||||
.lookup<NativeFunction<secret_spend_key>>('secret_spend_key')
|
||||
.asFunction<SecretSpendKey>();
|
||||
|
||||
final getPublicSpendKeyNative = moneroApi
|
||||
.lookup<NativeFunction<secret_view_key>>('public_spend_key')
|
||||
.asFunction<PublicSpendKey>();
|
||||
|
||||
final closeCurrentWalletNative = moneroApi
|
||||
.lookup<NativeFunction<close_current_wallet>>('close_current_wallet')
|
||||
.asFunction<CloseCurrentWallet>();
|
||||
|
||||
final onStartupNative = moneroApi
|
||||
.lookup<NativeFunction<on_startup>>('on_startup')
|
||||
.asFunction<OnStartup>();
|
||||
|
||||
final rescanBlockchainAsyncNative = moneroApi
|
||||
.lookup<NativeFunction<rescan_blockchain>>('rescan_blockchain')
|
||||
.asFunction<RescanBlockchainAsync>();
|
||||
|
||||
int getSyncingHeight() => getSyncingHeightNative();
|
||||
|
||||
bool isNeededToRefresh() => isNeededToRefreshNative() != 0;
|
||||
|
||||
bool isNewTransactionExist() => isNewTransactionExistNative() != 0;
|
||||
|
||||
String getFilename() => convertUTF8ToString(pointer: getFileNameNative());
|
||||
|
||||
String getSeed() => convertUTF8ToString(pointer: getSeedNative());
|
||||
|
||||
String getAddress({int accountIndex = 0, int addressIndex = 0}) =>
|
||||
convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex));
|
||||
|
||||
int getFullBalance({int accountIndex = 0}) =>
|
||||
getFullBalanceNative(accountIndex);
|
||||
|
||||
int getUnlockedBalance({int accountIndex = 0}) =>
|
||||
getUnlockedBalanceNative(accountIndex);
|
||||
|
||||
int getCurrentHeight() => getCurrentHeightNative();
|
||||
|
||||
int getNodeHeightSync() => getNodeHeightNative();
|
||||
|
||||
bool isConnectedSync() => isConnectedNative() != 0;
|
||||
|
||||
bool setupNodeSync(
|
||||
{String address,
|
||||
String login,
|
||||
String password,
|
||||
bool useSSL = false,
|
||||
bool isLightWallet = false}) {
|
||||
final addressPointer = Utf8.toUtf8(address);
|
||||
Pointer<Utf8> loginPointer;
|
||||
Pointer<Utf8> passwordPointer;
|
||||
|
||||
if (login != null) {
|
||||
loginPointer = Utf8.toUtf8(login);
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
passwordPointer = Utf8.toUtf8(password);
|
||||
}
|
||||
|
||||
final errorMessagePointer = allocate<Utf8>();
|
||||
final isSetupNode = setupNodeNative(
|
||||
addressPointer,
|
||||
loginPointer,
|
||||
passwordPointer,
|
||||
_boolToInt(useSSL),
|
||||
_boolToInt(isLightWallet),
|
||||
errorMessagePointer) !=
|
||||
0;
|
||||
|
||||
free(addressPointer);
|
||||
free(loginPointer);
|
||||
free(passwordPointer);
|
||||
|
||||
if (!isSetupNode) {
|
||||
throw SetupWalletException(
|
||||
message: convertUTF8ToString(pointer: errorMessagePointer));
|
||||
}
|
||||
|
||||
return isSetupNode;
|
||||
}
|
||||
|
||||
void startRefreshSync() => startRefreshNative();
|
||||
|
||||
Future<bool> connectToNode() async => connecToNodeNative() != 0;
|
||||
|
||||
void setRefreshFromBlockHeight({int height}) =>
|
||||
setRefreshFromBlockHeightNative(height);
|
||||
|
||||
void setRecoveringFromSeed({bool isRecovery}) =>
|
||||
setRecoveringFromSeedNative(_boolToInt(isRecovery));
|
||||
|
||||
void storeSync() {
|
||||
final pathPointer = Utf8.toUtf8('');
|
||||
storeNative(pathPointer);
|
||||
free(pathPointer);
|
||||
}
|
||||
|
||||
void closeCurrentWallet() => closeCurrentWalletNative();
|
||||
|
||||
String getSecretViewKey() =>
|
||||
convertUTF8ToString(pointer: getSecretViewKeyNative());
|
||||
|
||||
String getPublicViewKey() =>
|
||||
convertUTF8ToString(pointer: getPublicViewKeyNative());
|
||||
|
||||
String getSecretSpendKey() =>
|
||||
convertUTF8ToString(pointer: getSecretSpendKeyNative());
|
||||
|
||||
String getPublicSpendKey() =>
|
||||
convertUTF8ToString(pointer: getPublicSpendKeyNative());
|
||||
|
||||
class SyncListener {
|
||||
SyncListener(this.onNewBlock, this.onNewTransaction) {
|
||||
_cachedBlockchainHeight = 0;
|
||||
_lastKnownBlockHeight = 0;
|
||||
_initialSyncHeight = 0;
|
||||
}
|
||||
|
||||
void Function(int, int, double) onNewBlock;
|
||||
void Function() onNewTransaction;
|
||||
|
||||
Timer _updateSyncInfoTimer;
|
||||
int _cachedBlockchainHeight;
|
||||
int _lastKnownBlockHeight;
|
||||
int _initialSyncHeight;
|
||||
|
||||
Future<int> getNodeHeightOrUpdate(int baseHeight) async {
|
||||
if (_cachedBlockchainHeight < baseHeight || _cachedBlockchainHeight == 0) {
|
||||
_cachedBlockchainHeight = await getNodeHeight();
|
||||
}
|
||||
|
||||
return _cachedBlockchainHeight;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_cachedBlockchainHeight = 0;
|
||||
_lastKnownBlockHeight = 0;
|
||||
_initialSyncHeight = 0;
|
||||
_updateSyncInfoTimer ??=
|
||||
Timer.periodic(Duration(milliseconds: 1200), (_) async {
|
||||
if (isNewTransactionExist()) {
|
||||
onNewTransaction?.call();
|
||||
}
|
||||
|
||||
var syncHeight = getSyncingHeight();
|
||||
|
||||
if (syncHeight <= 0) {
|
||||
syncHeight = getCurrentHeight();
|
||||
}
|
||||
|
||||
if (_initialSyncHeight <= 0) {
|
||||
_initialSyncHeight = syncHeight;
|
||||
}
|
||||
|
||||
final bchHeight = await getNodeHeightOrUpdate(syncHeight);
|
||||
|
||||
if (_lastKnownBlockHeight == syncHeight || syncHeight == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lastKnownBlockHeight = syncHeight;
|
||||
final track = bchHeight - _initialSyncHeight;
|
||||
final diff = track - (bchHeight - syncHeight);
|
||||
final ptc = diff <= 0 ? 0.0 : diff / track;
|
||||
final left = bchHeight - syncHeight;
|
||||
|
||||
if (syncHeight < 0 || left < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents;
|
||||
onNewBlock?.call(syncHeight, left, ptc);
|
||||
});
|
||||
}
|
||||
|
||||
void stop() => _updateSyncInfoTimer?.cancel();
|
||||
}
|
||||
|
||||
SyncListener setListeners(void Function(int, int, double) onNewBlock,
|
||||
void Function() onNewTransaction) {
|
||||
final listener = SyncListener(onNewBlock, onNewTransaction);
|
||||
setListenerNative();
|
||||
return listener;
|
||||
}
|
||||
|
||||
void onStartup() => onStartupNative();
|
||||
|
||||
void _storeSync(Object _) => storeSync();
|
||||
|
||||
bool _setupNodeSync(Map args) {
|
||||
final address = args['address'] as String;
|
||||
final login = (args['login'] ?? '') as String;
|
||||
final password = (args['password'] ?? '') as String;
|
||||
final useSSL = args['useSSL'] as bool;
|
||||
final isLightWallet = args['isLightWallet'] as bool;
|
||||
|
||||
return setupNodeSync(
|
||||
address: address,
|
||||
login: login,
|
||||
password: password,
|
||||
useSSL: useSSL,
|
||||
isLightWallet: isLightWallet);
|
||||
}
|
||||
|
||||
bool _isConnected(Object _) => isConnectedSync();
|
||||
|
||||
int _getNodeHeight(Object _) => getNodeHeightSync();
|
||||
|
||||
void startRefresh() => startRefreshSync();
|
||||
|
||||
Future setupNode(
|
||||
{String address,
|
||||
String login,
|
||||
String password,
|
||||
bool useSSL = false,
|
||||
bool isLightWallet = false}) =>
|
||||
compute<Map<String, Object>, void>(_setupNodeSync, {
|
||||
'address': address,
|
||||
'login': login,
|
||||
'password': password,
|
||||
'useSSL': useSSL,
|
||||
'isLightWallet': isLightWallet
|
||||
});
|
||||
|
||||
Future store() => compute<int, void>(_storeSync, 0);
|
||||
|
||||
Future<bool> isConnected() => compute(_isConnected, 0);
|
||||
|
||||
Future<int> getNodeHeight() => compute(_getNodeHeight, 0);
|
||||
|
||||
void rescanBlockchainAsync() => rescanBlockchainAsyncNative();
|
|
@ -1,248 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_monero/convert_utf8_to_string.dart';
|
||||
import 'package:cw_monero/signatures.dart';
|
||||
import 'package:cw_monero/types.dart';
|
||||
import 'package:cw_monero/monero_api.dart';
|
||||
import 'package:cw_monero/wallet.dart';
|
||||
import 'package:cw_monero/exceptions/wallet_opening_exception.dart';
|
||||
import 'package:cw_monero/exceptions/wallet_creation_exception.dart';
|
||||
import 'package:cw_monero/exceptions/wallet_restore_from_keys_exception.dart';
|
||||
import 'package:cw_monero/exceptions/wallet_restore_from_seed_exception.dart';
|
||||
|
||||
final createWalletNative = moneroApi
|
||||
.lookup<NativeFunction<create_wallet>>('create_wallet')
|
||||
.asFunction<CreateWallet>();
|
||||
|
||||
final restoreWalletFromSeedNative = moneroApi
|
||||
.lookup<NativeFunction<restore_wallet_from_seed>>(
|
||||
'restore_wallet_from_seed')
|
||||
.asFunction<RestoreWalletFromSeed>();
|
||||
|
||||
final restoreWalletFromKeysNative = moneroApi
|
||||
.lookup<NativeFunction<restore_wallet_from_keys>>(
|
||||
'restore_wallet_from_keys')
|
||||
.asFunction<RestoreWalletFromKeys>();
|
||||
|
||||
final isWalletExistNative = moneroApi
|
||||
.lookup<NativeFunction<is_wallet_exist>>('is_wallet_exist')
|
||||
.asFunction<IsWalletExist>();
|
||||
|
||||
final loadWalletNative = moneroApi
|
||||
.lookup<NativeFunction<load_wallet>>('load_wallet')
|
||||
.asFunction<LoadWallet>();
|
||||
|
||||
final errorStringNative = moneroApi
|
||||
.lookup<NativeFunction<error_string>>('error_string')
|
||||
.asFunction<ErrorString>();
|
||||
|
||||
void createWalletSync(
|
||||
{String path, String password, String language, int nettype = 0}) {
|
||||
final pathPointer = Utf8.toUtf8(path);
|
||||
final passwordPointer = Utf8.toUtf8(password);
|
||||
final languagePointer = Utf8.toUtf8(language);
|
||||
final errorMessagePointer = allocate<Utf8>();
|
||||
final isWalletCreated = createWalletNative(pathPointer, passwordPointer,
|
||||
languagePointer, nettype, errorMessagePointer) !=
|
||||
0;
|
||||
|
||||
free(pathPointer);
|
||||
free(passwordPointer);
|
||||
free(languagePointer);
|
||||
|
||||
if (!isWalletCreated) {
|
||||
throw WalletCreationException(
|
||||
message: convertUTF8ToString(pointer: errorMessagePointer));
|
||||
}
|
||||
|
||||
// setupNodeSync(address: "node.moneroworld.com:18089");
|
||||
}
|
||||
|
||||
bool isWalletExistSync({String path}) {
|
||||
final pathPointer = Utf8.toUtf8(path);
|
||||
final isExist = isWalletExistNative(pathPointer) != 0;
|
||||
|
||||
free(pathPointer);
|
||||
|
||||
return isExist;
|
||||
}
|
||||
|
||||
void restoreWalletFromSeedSync(
|
||||
{String path,
|
||||
String password,
|
||||
String seed,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) {
|
||||
final pathPointer = Utf8.toUtf8(path);
|
||||
final passwordPointer = Utf8.toUtf8(password);
|
||||
final seedPointer = Utf8.toUtf8(seed);
|
||||
final errorMessagePointer = allocate<Utf8>();
|
||||
final isWalletRestored = restoreWalletFromSeedNative(
|
||||
pathPointer,
|
||||
passwordPointer,
|
||||
seedPointer,
|
||||
nettype,
|
||||
restoreHeight,
|
||||
errorMessagePointer) !=
|
||||
0;
|
||||
|
||||
free(pathPointer);
|
||||
free(passwordPointer);
|
||||
free(seedPointer);
|
||||
|
||||
if (!isWalletRestored) {
|
||||
throw WalletRestoreFromSeedException(
|
||||
message: convertUTF8ToString(pointer: errorMessagePointer));
|
||||
}
|
||||
}
|
||||
|
||||
void restoreWalletFromKeysSync(
|
||||
{String path,
|
||||
String password,
|
||||
String language,
|
||||
String address,
|
||||
String viewKey,
|
||||
String spendKey,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) {
|
||||
final pathPointer = Utf8.toUtf8(path);
|
||||
final passwordPointer = Utf8.toUtf8(password);
|
||||
final languagePointer = Utf8.toUtf8(language);
|
||||
final addressPointer = Utf8.toUtf8(address);
|
||||
final viewKeyPointer = Utf8.toUtf8(viewKey);
|
||||
final spendKeyPointer = Utf8.toUtf8(spendKey);
|
||||
final errorMessagePointer = allocate<Utf8>();
|
||||
final isWalletRestored = restoreWalletFromKeysNative(
|
||||
pathPointer,
|
||||
passwordPointer,
|
||||
languagePointer,
|
||||
addressPointer,
|
||||
viewKeyPointer,
|
||||
spendKeyPointer,
|
||||
nettype,
|
||||
restoreHeight,
|
||||
errorMessagePointer) !=
|
||||
0;
|
||||
|
||||
free(pathPointer);
|
||||
free(passwordPointer);
|
||||
free(languagePointer);
|
||||
free(addressPointer);
|
||||
free(viewKeyPointer);
|
||||
free(spendKeyPointer);
|
||||
|
||||
if (!isWalletRestored) {
|
||||
throw WalletRestoreFromKeysException(
|
||||
message: convertUTF8ToString(pointer: errorMessagePointer));
|
||||
}
|
||||
}
|
||||
|
||||
void loadWallet({String path, String password, int nettype = 0}) {
|
||||
final pathPointer = Utf8.toUtf8(path);
|
||||
final passwordPointer = Utf8.toUtf8(password);
|
||||
final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0;
|
||||
free(pathPointer);
|
||||
free(passwordPointer);
|
||||
|
||||
if (!loaded) {
|
||||
throw WalletOpeningException(
|
||||
message: convertUTF8ToString(pointer: errorStringNative()));
|
||||
}
|
||||
}
|
||||
|
||||
void _createWallet(Map<String, dynamic> args) {
|
||||
final path = args['path'] as String;
|
||||
final password = args['password'] as String;
|
||||
final language = args['language'] as String;
|
||||
|
||||
createWalletSync(path: path, password: password, language: language);
|
||||
}
|
||||
|
||||
void _restoreFromSeed(Map<String, dynamic> args) {
|
||||
final path = args['path'] as String;
|
||||
final password = args['password'] as String;
|
||||
final seed = args['seed'] as String;
|
||||
final restoreHeight = args['restoreHeight'] as int;
|
||||
|
||||
restoreWalletFromSeedSync(
|
||||
path: path, password: password, seed: seed, restoreHeight: restoreHeight);
|
||||
}
|
||||
|
||||
void _restoreFromKeys(Map<String, dynamic> args) {
|
||||
final path = args['path'] as String;
|
||||
final password = args['password'] as String;
|
||||
final language = args['language'] as String;
|
||||
final restoreHeight = args['restoreHeight'] as int;
|
||||
final address = args['address'] as String;
|
||||
final viewKey = args['viewKey'] as String;
|
||||
final spendKey = args['spendKey'] as String;
|
||||
|
||||
restoreWalletFromKeysSync(
|
||||
path: path,
|
||||
password: password,
|
||||
language: language,
|
||||
restoreHeight: restoreHeight,
|
||||
address: address,
|
||||
viewKey: viewKey,
|
||||
spendKey: spendKey);
|
||||
}
|
||||
|
||||
Future<void> _openWallet(Map<String, String> args) async =>
|
||||
loadWallet(path: args['path'], password: args['password']);
|
||||
|
||||
bool _isWalletExist(String path) => isWalletExistSync(path: path);
|
||||
|
||||
void openWallet({String path, String password, int nettype = 0}) async =>
|
||||
loadWallet(path: path, password: password, nettype: nettype);
|
||||
|
||||
Future<void> openWalletAsync(Map<String, String> args) async =>
|
||||
compute(_openWallet, args);
|
||||
|
||||
Future<void> createWallet(
|
||||
{String path,
|
||||
String password,
|
||||
String language,
|
||||
int nettype = 0}) async =>
|
||||
compute(_createWallet, {
|
||||
'path': path,
|
||||
'password': password,
|
||||
'language': language,
|
||||
'nettype': nettype
|
||||
});
|
||||
|
||||
Future restoreFromSeed(
|
||||
{String path,
|
||||
String password,
|
||||
String seed,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) async =>
|
||||
compute<Map<String, Object>, void>(_restoreFromSeed, {
|
||||
'path': path,
|
||||
'password': password,
|
||||
'seed': seed,
|
||||
'nettype': nettype,
|
||||
'restoreHeight': restoreHeight
|
||||
});
|
||||
|
||||
Future restoreFromKeys(
|
||||
{String path,
|
||||
String password,
|
||||
String language,
|
||||
String address,
|
||||
String viewKey,
|
||||
String spendKey,
|
||||
int nettype = 0,
|
||||
int restoreHeight = 0}) async =>
|
||||
compute<Map<String, Object>, void>(_restoreFromKeys, {
|
||||
'path': path,
|
||||
'password': password,
|
||||
'language': language,
|
||||
'address': address,
|
||||
'viewKey': viewKey,
|
||||
'spendKey': spendKey,
|
||||
'nettype': nettype,
|
||||
'restoreHeight': restoreHeight
|
||||
});
|
||||
|
||||
Future<bool> isWalletExist({String path}) => compute(_isWalletExist, path);
|
|
@ -1,6 +1,27 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "14.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.41.2"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -15,6 +36,62 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.2"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.6"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.10"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.3"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.5"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.10"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.1.3"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -29,6 +106,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -36,6 +127,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -43,6 +141,41 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
cw_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../cw_core"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.12"
|
||||
dartx:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dartx
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -57,16 +190,121 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.2"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_mobx:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_mobx
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0+2"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
hive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.0-nullsafety.2"
|
||||
hive_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive_generator
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.2"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.2"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.17.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.3"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -81,6 +319,34 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
mobx:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mobx
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1+4"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.3"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -95,6 +361,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -102,11 +375,53 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.8"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.9"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.4+1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.10+3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -128,6 +443,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -149,6 +471,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.19"
|
||||
time:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: time
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1+3"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -163,6 +499,27 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
sdks:
|
||||
dart: ">=2.12.0-0.0 <3.0.0"
|
||||
flutter: ">=0.1.4"
|
||||
dart: ">=2.12.0 <3.0.0"
|
||||
flutter: ">=1.17.0"
|
||||
|
|
|
@ -12,10 +12,20 @@ dependencies:
|
|||
sdk: flutter
|
||||
ffi: ^0.1.3
|
||||
path_provider: ^1.4.0
|
||||
http: ^0.12.0+2
|
||||
mobx: ^1.2.1+2
|
||||
flutter_mobx: ^1.1.0+2
|
||||
intl: ^0.17.0
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^1.10.3
|
||||
build_resolvers: ^1.3.10
|
||||
mobx_codegen: ^1.1.0+1
|
||||
hive_generator: ^0.8.1
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
|
@ -196,4 +196,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: bc2591d23316907c9c90ca1cd2fce063fd866508
|
||||
|
||||
COCOAPODS: 1.9.3
|
||||
COCOAPODS: 1.10.2
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.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) {
|
||||
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 {
|
||||
// FIXME: improve validation for p2sh addresses
|
||||
// 3 for bitcoin
|
||||
// m for litecoin
|
||||
if (address.startsWith('3') || address.toLowerCase().startsWith('m')) {
|
||||
return p2shAddressToOutputScript(address);
|
||||
}
|
||||
|
||||
return Address.addressToOutputScript(address, networkType);
|
||||
} catch (err) {
|
||||
print(err);
|
||||
return Uint8List(0);
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import 'dart:convert';
|
||||
|
||||
class BitcoinAddressRecord {
|
||||
BitcoinAddressRecord(this.address, {this.index, bool isHidden})
|
||||
: _isHidden = isHidden;
|
||||
|
||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
return BitcoinAddressRecord(decoded['address'] as String,
|
||||
index: decoded['index'] as int, isHidden: decoded['isHidden'] as bool);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) =>
|
||||
o is BitcoinAddressRecord && address == o.address;
|
||||
|
||||
final String address;
|
||||
bool get isHidden => _isHidden ?? false;
|
||||
int index;
|
||||
final bool _isHidden;
|
||||
|
||||
@override
|
||||
int get hashCode => address.hashCode;
|
||||
|
||||
String toJSON() =>
|
||||
json.encode({'address': address, 'index': index, 'isHidden': isHidden});
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cake_wallet/entities/crypto_amount_format.dart';
|
||||
|
||||
const bitcoinAmountLength = 8;
|
||||
const bitcoinAmountDivider = 100000000;
|
||||
final bitcoinAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = bitcoinAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
|
||||
String bitcoinAmountToString({int amount}) => bitcoinAmountFormat.format(
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
|
||||
|
||||
double bitcoinAmountToDouble({int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
|
||||
int stringDoubleToBitcoinAmount(String amount) {
|
||||
int result = 0;
|
||||
|
||||
try {
|
||||
result = (double.parse(amount) * bitcoinAmountDivider).toInt();
|
||||
} catch (e) {
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
class BitcoinCommitTransactionException implements Exception {
|
||||
@override
|
||||
String toString() => 'Transaction commit is failed.';
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +0,0 @@
|
|||
class BitcoinMnemonicIsIncorrectException implements Exception {
|
||||
@override
|
||||
String toString() =>
|
||||
'Bitcoin mnemonic has incorrect format. Mnemonic should contain 12 or 24 words separated by space.';
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cake_wallet/view_model/send/output.dart';
|
||||
|
||||
class BitcoinTransactionCredentials {
|
||||
BitcoinTransactionCredentials(this.outputs, this.priority);
|
||||
|
||||
final List<Output> outputs;
|
||||
BitcoinTransactionPriority priority;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
class BitcoinTransactionNoInputsException implements Exception {
|
||||
@override
|
||||
String toString() => 'Not enough inputs available';
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class BitcoinTransactionPriority extends TransactionPriority {
|
||||
const BitcoinTransactionPriority({String title, int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const List<BitcoinTransactionPriority> all = [fast, medium, slow];
|
||||
static const BitcoinTransactionPriority slow =
|
||||
BitcoinTransactionPriority(title: 'Slow', raw: 0);
|
||||
static const BitcoinTransactionPriority medium =
|
||||
BitcoinTransactionPriority(title: 'Medium', raw: 1);
|
||||
static const BitcoinTransactionPriority fast =
|
||||
BitcoinTransactionPriority(title: 'Fast', raw: 2);
|
||||
|
||||
static BitcoinTransactionPriority deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return slow;
|
||||
case 1:
|
||||
return medium;
|
||||
case 2:
|
||||
return fast;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String get units => 'sat';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var label = '';
|
||||
|
||||
switch (this) {
|
||||
case BitcoinTransactionPriority.slow:
|
||||
label = '${S.current.transaction_priority_slow} ~24hrs';
|
||||
break;
|
||||
case BitcoinTransactionPriority.medium:
|
||||
label = S.current.transaction_priority_medium;
|
||||
break;
|
||||
case BitcoinTransactionPriority.fast:
|
||||
label = S.current.transaction_priority_fast;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
String labelWithRate(int rate) => '${toString()} ($rate ${units}/byte)';
|
||||
}
|
||||
|
||||
class LitecoinTransactionPriority extends BitcoinTransactionPriority {
|
||||
const LitecoinTransactionPriority({String title, int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const List<LitecoinTransactionPriority> all = [fast, medium, slow];
|
||||
static const LitecoinTransactionPriority slow =
|
||||
LitecoinTransactionPriority(title: 'Slow', raw: 0);
|
||||
static const LitecoinTransactionPriority medium =
|
||||
LitecoinTransactionPriority(title: 'Medium', raw: 1);
|
||||
static const LitecoinTransactionPriority fast =
|
||||
LitecoinTransactionPriority(title: 'Fast', raw: 2);
|
||||
|
||||
static LitecoinTransactionPriority deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return slow;
|
||||
case 1:
|
||||
return medium;
|
||||
case 2:
|
||||
return fast;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String get units => 'Latoshi';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var label = '';
|
||||
|
||||
switch (this) {
|
||||
case LitecoinTransactionPriority.slow:
|
||||
label = S.current.transaction_priority_slow;
|
||||
break;
|
||||
case LitecoinTransactionPriority.medium:
|
||||
label = S.current.transaction_priority_medium;
|
||||
break;
|
||||
case LitecoinTransactionPriority.fast:
|
||||
label = S.current.transaction_priority_fast;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
|
||||
class BitcoinTransactionWrongBalanceException implements Exception {
|
||||
BitcoinTransactionWrongBalanceException(this.currency);
|
||||
|
||||
final CryptoCurrency currency;
|
||||
|
||||
@override
|
||||
String toString() => 'Wrong balance. Not enough ${currency.title} on your balance.';
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
|
||||
class BitcoinUnspent {
|
||||
BitcoinUnspent(this.address, this.hash, this.value, this.vout)
|
||||
: isSending = true,
|
||||
isFrozen = false,
|
||||
note = '';
|
||||
|
||||
factory BitcoinUnspent.fromJSON(
|
||||
BitcoinAddressRecord address, Map<String, dynamic> json) =>
|
||||
BitcoinUnspent(address, json['tx_hash'] as String, json['value'] as int,
|
||||
json['tx_pos'] as int);
|
||||
|
||||
final BitcoinAddressRecord address;
|
||||
final String hash;
|
||||
final int value;
|
||||
final int vout;
|
||||
|
||||
bool get isP2wpkh =>
|
||||
address.address.startsWith('bc') || address.address.startsWith('ltc');
|
||||
bool isSending;
|
||||
bool isFrozen;
|
||||
String note;
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_balance.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_addresses.dart';
|
||||
|
||||
part 'bitcoin_wallet.g.dart';
|
||||
|
||||
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
||||
|
||||
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
BitcoinWalletBase(
|
||||
{@required String mnemonic,
|
||||
@required String password,
|
||||
@required WalletInfo walletInfo,
|
||||
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
List<BitcoinAddressRecord> initialAddresses,
|
||||
ElectrumBalance initialBalance,
|
||||
int accountIndex = 0})
|
||||
: super(
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: bitcoin.bitcoin,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance) {
|
||||
walletAddresses = BitcoinWalletAddresses(
|
||||
walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
accountIndex: accountIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet.fromSeed(
|
||||
mnemonicToSeedBytes(mnemonic), network: networkType)
|
||||
.derivePath("m/0'/1"),
|
||||
networkType: networkType);
|
||||
}
|
||||
|
||||
static Future<BitcoinWallet> open({
|
||||
@required String name,
|
||||
@required WalletInfo walletInfo,
|
||||
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
@required String password,
|
||||
}) async {
|
||||
final snp = ElectrumWallletSnapshot(name, walletInfo.type, password);
|
||||
await snp.load();
|
||||
return BitcoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp.addresses,
|
||||
initialBalance: snp.balance,
|
||||
accountIndex: snp.accountIndex);
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/bitcoin/utils.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'bitcoin_wallet_addresses.g.dart';
|
||||
|
||||
class BitcoinWalletAddresses = BitcoinWalletAddressesBase
|
||||
with _$BitcoinWalletAddresses;
|
||||
|
||||
abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses
|
||||
with Store {
|
||||
BitcoinWalletAddressesBase(
|
||||
WalletInfo walletInfo,
|
||||
{@required List<BitcoinAddressRecord> initialAddresses,
|
||||
int accountIndex = 0,
|
||||
@required bitcoin.HDWallet mainHd,
|
||||
@required bitcoin.HDWallet sideHd,
|
||||
@required this.networkType})
|
||||
: super(
|
||||
walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
accountIndex: accountIndex,
|
||||
mainHd: mainHd,
|
||||
sideHd: sideHd);
|
||||
|
||||
bitcoin.NetworkType networkType;
|
||||
|
||||
@override
|
||||
String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
|
||||
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import 'package:cake_wallet/core/wallet_credentials.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
|
||||
class BitcoinNewWalletCredentials extends WalletCredentials {
|
||||
BitcoinNewWalletCredentials({String name, WalletInfo walletInfo})
|
||||
: super(name: name, walletInfo: walletInfo);
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromSeedCredentials(
|
||||
{String name, String password, this.mnemonic, WalletInfo walletInfo})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String mnemonic;
|
||||
}
|
||||
|
||||
class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||
BitcoinRestoreWalletFromWIFCredentials(
|
||||
{String name, String password, this.wif, WalletInfo walletInfo})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String wif;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class BitcoinWalletKeys {
|
||||
const BitcoinWalletKeys({@required this.wif, @required this.privateKey, @required this.publicKey});
|
||||
|
||||
final String wif;
|
||||
final String privateKey;
|
||||
final String publicKey;
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
import 'dart:io';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
|
||||
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
||||
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
class BitcoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials> {
|
||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
||||
|
||||
@override
|
||||
WalletType getType() => WalletType.bitcoin;
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
||||
final wallet = BitcoinWallet(
|
||||
mnemonic: await generateMnemonic(),
|
||||
password: credentials.password,
|
||||
walletInfo: credentials.walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isWalletExit(String name) async =>
|
||||
File(await pathForWallet(name: name, type: getType())).existsSync();
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> openWallet(String name, String password) async {
|
||||
final walletInfo = walletInfoSource.values.firstWhere(
|
||||
(info) => info.id == WalletBase.idFor(name, getType()),
|
||||
orElse: () => null);
|
||||
final wallet = await BitcoinWalletBase.open(
|
||||
password: password, name: name, walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> remove(String wallet) async =>
|
||||
File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin))
|
||||
.delete(recursive: true);
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromKeys(
|
||||
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromSeed(
|
||||
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||
if (!validateMnemonic(credentials.mnemonic)) {
|
||||
throw BitcoinMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
final wallet = BitcoinWallet(
|
||||
password: credentials.password,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
}
|
|
@ -1,444 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/bitcoin/script_hash.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
String jsonrpcparams(List<Object> params) {
|
||||
final _params = params?.map((val) => '"${val.toString()}"')?.join(',');
|
||||
return '[$_params]';
|
||||
}
|
||||
|
||||
String jsonrpc(
|
||||
{String method, List<Object> params, int id, double version = 2.0}) =>
|
||||
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
|
||||
|
||||
class SocketTask {
|
||||
SocketTask({this.completer, this.isSubscription, this.subject});
|
||||
|
||||
final Completer completer;
|
||||
final BehaviorSubject subject;
|
||||
final bool isSubscription;
|
||||
}
|
||||
|
||||
class ElectrumClient {
|
||||
ElectrumClient()
|
||||
: _id = 0,
|
||||
_isConnected = false,
|
||||
_tasks = {},
|
||||
unterminatedString = '';
|
||||
|
||||
static const connectionTimeout = Duration(seconds: 5);
|
||||
static const aliveTimerDuration = Duration(seconds: 2);
|
||||
|
||||
bool get isConnected => _isConnected;
|
||||
Socket socket;
|
||||
void Function(bool) onConnectionStatusChange;
|
||||
int _id;
|
||||
final Map<String, SocketTask> _tasks;
|
||||
bool _isConnected;
|
||||
Timer _aliveTimer;
|
||||
String unterminatedString;
|
||||
|
||||
Future<void> connectToUri(Uri uri) async =>
|
||||
await connect(host: uri.host, port: uri.port);
|
||||
|
||||
Future<void> connect({@required String host, @required int port}) async {
|
||||
try {
|
||||
await socket?.close();
|
||||
} catch (_) {}
|
||||
|
||||
socket = await SecureSocket.connect(host, port,
|
||||
timeout: connectionTimeout, onBadCertificate: (_) => true);
|
||||
_setIsConnected(true);
|
||||
|
||||
socket.listen((Uint8List event) {
|
||||
try {
|
||||
final response =
|
||||
json.decode(utf8.decode(event.toList())) as Map<String, Object>;
|
||||
_handleResponse(response);
|
||||
} on FormatException catch (e) {
|
||||
final msg = e.message.toLowerCase();
|
||||
|
||||
if (e.source is String) {
|
||||
unterminatedString += e.source as String;
|
||||
}
|
||||
|
||||
if (msg.contains("not a subtype of type")) {
|
||||
unterminatedString += e.source as String;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isJSONStringCorrect(unterminatedString)) {
|
||||
final response =
|
||||
json.decode(unterminatedString) as Map<String, Object>;
|
||||
_handleResponse(response);
|
||||
unterminatedString = '';
|
||||
}
|
||||
} on TypeError catch (e) {
|
||||
if (!e.toString().contains('Map<String, Object>')) {
|
||||
return;
|
||||
}
|
||||
|
||||
final source = utf8.decode(event.toList());
|
||||
unterminatedString += source;
|
||||
|
||||
if (isJSONStringCorrect(unterminatedString)) {
|
||||
final response =
|
||||
json.decode(unterminatedString) as Map<String, Object>;
|
||||
_handleResponse(response);
|
||||
unterminatedString = null;
|
||||
}
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}, onError: (Object error) {
|
||||
print(error.toString());
|
||||
_setIsConnected(false);
|
||||
}, onDone: () {
|
||||
_setIsConnected(false);
|
||||
});
|
||||
keepAlive();
|
||||
}
|
||||
|
||||
void keepAlive() {
|
||||
_aliveTimer?.cancel();
|
||||
_aliveTimer = Timer.periodic(aliveTimerDuration, (_) async => ping());
|
||||
}
|
||||
|
||||
Future<void> ping() async {
|
||||
try {
|
||||
await callWithTimeout(method: 'server.ping');
|
||||
_setIsConnected(true);
|
||||
} on RequestFailedTimeoutException catch (_) {
|
||||
_setIsConnected(false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<String>> version() =>
|
||||
call(method: 'server.version').then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) => val.toString()).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<Map<String, Object>> getBalance(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.get_balance', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is Map<String, Object>) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getHistory(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.get_history', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, Object>) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
||||
String address, NetworkType networkType) =>
|
||||
call(
|
||||
method: 'blockchain.scripthash.listunspent',
|
||||
params: [scriptHash(address, networkType: networkType)])
|
||||
.then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, Object>) {
|
||||
val['address'] = address;
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.listunspent', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, Object>) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getMempool(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.get_mempool', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, Object>) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<Map<String, Object>> getTransactionRaw(
|
||||
{@required String hash}) async =>
|
||||
call(method: 'blockchain.transaction.get', params: [hash, true])
|
||||
.then((dynamic result) {
|
||||
if (result is Map<String, Object>) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
});
|
||||
|
||||
Future<Map<String, Object>> getTransactionExpanded(
|
||||
{@required String hash}) async {
|
||||
try {
|
||||
final originalTx = await getTransactionRaw(hash: hash);
|
||||
final vins = originalTx['vin'] as List<Object>;
|
||||
|
||||
for (dynamic vin in vins) {
|
||||
if (vin is Map<String, Object>) {
|
||||
vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String);
|
||||
}
|
||||
}
|
||||
|
||||
return originalTx;
|
||||
} catch (_) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> broadcastTransaction(
|
||||
{@required String transactionRaw}) async =>
|
||||
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
||||
.then((dynamic result) {
|
||||
if (result is String) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
Future<Map<String, dynamic>> getMerkle(
|
||||
{@required String hash, @required int height}) async =>
|
||||
await call(
|
||||
method: 'blockchain.transaction.get_merkle',
|
||||
params: [hash, height]) as Map<String, dynamic>;
|
||||
|
||||
Future<Map<String, dynamic>> getHeader({@required int height}) async =>
|
||||
await call(method: 'blockchain.block.get_header', params: [height])
|
||||
as Map<String, dynamic>;
|
||||
|
||||
Future<double> estimatefee({@required int p}) =>
|
||||
call(method: 'blockchain.estimatefee', params: [p])
|
||||
.then((dynamic result) {
|
||||
if (result is double) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result is String) {
|
||||
return double.parse(result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
Future<List<List<int>>> feeHistogram() =>
|
||||
call(method: 'mempool.get_fee_histogram').then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic e) {
|
||||
if (e is List) {
|
||||
return e.map((dynamic ee) => ee is int ? ee : null).toList();
|
||||
}
|
||||
|
||||
return null;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<List<int>> feeRates() async {
|
||||
try {
|
||||
final topDoubleString = await estimatefee(p: 1);
|
||||
final middleDoubleString = await estimatefee(p: 20);
|
||||
final bottomDoubleString = await estimatefee(p: 100);
|
||||
final top =
|
||||
(stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000)
|
||||
.round();
|
||||
final middle =
|
||||
(stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000)
|
||||
.round();
|
||||
final bottom =
|
||||
(stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000)
|
||||
.round();
|
||||
|
||||
return [bottom, middle, top];
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorSubject<Object> scripthashUpdate(String scripthash) {
|
||||
_id += 1;
|
||||
return subscribe<Object>(
|
||||
id: 'blockchain.scripthash.subscribe:$scripthash',
|
||||
method: 'blockchain.scripthash.subscribe',
|
||||
params: [scripthash]);
|
||||
}
|
||||
|
||||
BehaviorSubject<T> subscribe<T>(
|
||||
{@required String id,
|
||||
@required String method,
|
||||
List<Object> params = const []}) {
|
||||
try {
|
||||
final subscription = BehaviorSubject<T>();
|
||||
_regisrySubscription(id, subscription);
|
||||
socket.write(jsonrpc(method: method, id: _id, params: params));
|
||||
|
||||
return subscription;
|
||||
} catch(e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> call({String method, List<Object> params = const []}) async {
|
||||
final completer = Completer<dynamic>();
|
||||
_id += 1;
|
||||
final id = _id;
|
||||
_registryTask(id, completer);
|
||||
socket.write(jsonrpc(method: method, id: id, params: params));
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<dynamic> callWithTimeout(
|
||||
{String method,
|
||||
List<Object> params = const [],
|
||||
int timeout = 2000}) async {
|
||||
try {
|
||||
final completer = Completer<dynamic>();
|
||||
_id += 1;
|
||||
final id = _id;
|
||||
_registryTask(id, completer);
|
||||
socket.write(jsonrpc(method: method, id: id, params: params));
|
||||
Timer(Duration(milliseconds: timeout), () {
|
||||
if (!completer.isCompleted) {
|
||||
completer.completeError(RequestFailedTimeoutException(method, id));
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
} catch(e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
_aliveTimer.cancel();
|
||||
await socket.close();
|
||||
onConnectionStatusChange = null;
|
||||
}
|
||||
|
||||
void _registryTask(int id, Completer completer) => _tasks[id.toString()] =
|
||||
SocketTask(completer: completer, isSubscription: false);
|
||||
|
||||
void _regisrySubscription(String id, BehaviorSubject subject) =>
|
||||
_tasks[id] = SocketTask(subject: subject, isSubscription: true);
|
||||
|
||||
void _finish(String id, Object data) {
|
||||
if (_tasks[id] == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(_tasks[id]?.completer?.isCompleted ?? false)) {
|
||||
_tasks[id]?.completer?.complete(data);
|
||||
}
|
||||
|
||||
if (!(_tasks[id]?.isSubscription ?? false)) {
|
||||
_tasks[id] = null;
|
||||
} else {
|
||||
_tasks[id].subject.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
void _methodHandler(
|
||||
{@required String method, @required Map<String, Object> request}) {
|
||||
switch (method) {
|
||||
case 'blockchain.scripthash.subscribe':
|
||||
final params = request['params'] as List<dynamic>;
|
||||
final scripthash = params.first as String;
|
||||
final id = 'blockchain.scripthash.subscribe:$scripthash';
|
||||
|
||||
_tasks[id]?.subject?.add(params.last);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _setIsConnected(bool isConnected) {
|
||||
if (_isConnected != isConnected) {
|
||||
onConnectionStatusChange?.call(isConnected);
|
||||
}
|
||||
|
||||
_isConnected = isConnected;
|
||||
}
|
||||
|
||||
void _handleResponse(Map<String, Object> response) {
|
||||
final method = response['method'];
|
||||
final id = response['id'] as String;
|
||||
final result = response['result'];
|
||||
|
||||
if (method is String) {
|
||||
_methodHandler(method: method, request: response);
|
||||
return;
|
||||
}
|
||||
|
||||
_finish(id, result);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: move me
|
||||
bool isJSONStringCorrect(String source) {
|
||||
try {
|
||||
json.decode(source);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class RequestFailedTimeoutException implements Exception {
|
||||
RequestFailedTimeoutException(this.method, this.id);
|
||||
|
||||
final String method;
|
||||
final int id;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/entities/balance.dart';
|
||||
|
||||
class ElectrumBalance extends Balance {
|
||||
const ElectrumBalance({@required this.confirmed, @required this.unconfirmed})
|
||||
: super(confirmed, unconfirmed);
|
||||
|
||||
factory ElectrumBalance.fromJSON(String jsonSource) {
|
||||
if (jsonSource == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
return ElectrumBalance(
|
||||
confirmed: decoded['confirmed'] as int ?? 0,
|
||||
unconfirmed: decoded['unconfirmed'] as int ?? 0);
|
||||
}
|
||||
|
||||
final int confirmed;
|
||||
final int unconfirmed;
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance =>
|
||||
bitcoinAmountToString(amount: confirmed);
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance =>
|
||||
bitcoinAmountToString(amount: unconfirmed);
|
||||
|
||||
String toJSON() =>
|
||||
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed});
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/core/transaction_history.dart';
|
||||
import 'package:cake_wallet/bitcoin/file.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_transaction_info.dart';
|
||||
|
||||
part 'electrum_transaction_history.g.dart';
|
||||
|
||||
const _transactionsHistoryFileName = 'transactions.json';
|
||||
|
||||
class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
|
||||
with _$ElectrumTransactionHistory;
|
||||
|
||||
abstract class ElectrumTransactionHistoryBase
|
||||
extends TransactionHistoryBase<ElectrumTransactionInfo> with Store {
|
||||
ElectrumTransactionHistoryBase(
|
||||
{@required this.walletInfo, @required String password})
|
||||
: _password = password,
|
||||
_height = 0 {
|
||||
transactions = ObservableMap<String, ElectrumTransactionInfo>();
|
||||
}
|
||||
|
||||
final WalletInfo walletInfo;
|
||||
final String _password;
|
||||
int _height;
|
||||
|
||||
Future<void> init() async => await _load();
|
||||
|
||||
@override
|
||||
void addOne(ElectrumTransactionInfo transaction) =>
|
||||
transactions[transaction.id] = transaction;
|
||||
|
||||
@override
|
||||
void addMany(Map<String, ElectrumTransactionInfo> transactions) =>
|
||||
transactions.forEach((_, tx) => _updateOrInsert(tx));
|
||||
|
||||
@override
|
||||
Future<void> save() async {
|
||||
try {
|
||||
final dirPath =
|
||||
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||
final path = '$dirPath/$_transactionsHistoryFileName';
|
||||
final data =
|
||||
json.encode({'height': _height, 'transactions': transactions});
|
||||
await writeData(path: path, password: _password, data: data);
|
||||
} catch (e) {
|
||||
print('Error while save bitcoin transaction history: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, Object>> _read() async {
|
||||
final dirPath =
|
||||
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
|
||||
final path = '$dirPath/$_transactionsHistoryFileName';
|
||||
final content = await read(path: path, password: _password);
|
||||
return json.decode(content) as Map<String, Object>;
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
try {
|
||||
final content = await _read();
|
||||
final txs = content['transactions'] as Map<String, Object> ?? {};
|
||||
|
||||
txs.entries.forEach((entry) {
|
||||
final val = entry.value;
|
||||
|
||||
if (val is Map<String, Object>) {
|
||||
final tx = ElectrumTransactionInfo.fromJson(val, walletInfo.type);
|
||||
_updateOrInsert(tx);
|
||||
}
|
||||
});
|
||||
|
||||
_height = content['height'] as int;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateOrInsert(ElectrumTransactionInfo transaction) {
|
||||
if (transaction.id == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (transactions[transaction.id] == null) {
|
||||
transactions[transaction.id] = transaction;
|
||||
} else {
|
||||
final originalTx = transactions[transaction.id];
|
||||
originalTx.confirmations = transaction.confirmations;
|
||||
originalTx.amount = transaction.amount;
|
||||
originalTx.height = transaction.height;
|
||||
originalTx.date ??= transaction.date;
|
||||
originalTx.isPending = transaction.isPending;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/entities/transaction_direction.dart';
|
||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||
import 'package:cake_wallet/entities/format_amount.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
class ElectrumTransactionInfo extends TransactionInfo {
|
||||
ElectrumTransactionInfo(this.type,
|
||||
{@required String id,
|
||||
@required int height,
|
||||
@required int amount,
|
||||
@required int fee,
|
||||
@required TransactionDirection direction,
|
||||
@required bool isPending,
|
||||
@required DateTime date,
|
||||
@required int confirmations}) {
|
||||
this.id = id;
|
||||
this.height = height;
|
||||
this.amount = amount;
|
||||
this.fee = fee;
|
||||
this.direction = direction;
|
||||
this.date = date;
|
||||
this.isPending = isPending;
|
||||
this.confirmations = confirmations;
|
||||
}
|
||||
|
||||
factory ElectrumTransactionInfo.fromElectrumVerbose(
|
||||
Map<String, Object> obj, WalletType type,
|
||||
{@required List<BitcoinAddressRecord> addresses, @required int height}) {
|
||||
final addressesSet = addresses.map((addr) => addr.address).toSet();
|
||||
final id = obj['txid'] as String;
|
||||
final vins = obj['vin'] as List<Object> ?? [];
|
||||
final vout = (obj['vout'] as List<Object> ?? []);
|
||||
final date = obj['time'] is int
|
||||
? DateTime.fromMillisecondsSinceEpoch((obj['time'] as int) * 1000)
|
||||
: DateTime.now();
|
||||
final confirmations = obj['confirmations'] as int ?? 0;
|
||||
var direction = TransactionDirection.incoming;
|
||||
var inputsAmount = 0;
|
||||
var amount = 0;
|
||||
var totalOutAmount = 0;
|
||||
|
||||
for (dynamic vin in vins) {
|
||||
final vout = vin['vout'] as int;
|
||||
final out = vin['tx']['vout'][vout] as Map;
|
||||
final outAddresses =
|
||||
(out['scriptPubKey']['addresses'] as List<Object>)?.toSet();
|
||||
inputsAmount +=
|
||||
stringDoubleToBitcoinAmount((out['value'] as double ?? 0).toString());
|
||||
|
||||
if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) {
|
||||
direction = TransactionDirection.outgoing;
|
||||
}
|
||||
}
|
||||
|
||||
for (dynamic out in vout) {
|
||||
final outAddresses =
|
||||
out['scriptPubKey']['addresses'] as List<Object> ?? [];
|
||||
final ntrs = outAddresses.toSet().intersection(addressesSet);
|
||||
final value = stringDoubleToBitcoinAmount(
|
||||
(out['value'] as double ?? 0.0).toString());
|
||||
totalOutAmount += value;
|
||||
|
||||
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||
|
||||
(direction == TransactionDirection.outgoing && ntrs.isEmpty)) {
|
||||
amount += value;
|
||||
}
|
||||
}
|
||||
|
||||
final fee = inputsAmount - totalOutAmount;
|
||||
|
||||
return ElectrumTransactionInfo(type,
|
||||
id: id,
|
||||
height: height,
|
||||
isPending: false,
|
||||
fee: fee,
|
||||
direction: direction,
|
||||
amount: amount,
|
||||
date: date,
|
||||
confirmations: confirmations);
|
||||
}
|
||||
|
||||
factory ElectrumTransactionInfo.fromHexAndHeader(WalletType type, String hex,
|
||||
{List<String> addresses, int height, int timestamp, int confirmations}) {
|
||||
final tx = bitcoin.Transaction.fromHex(hex);
|
||||
var exist = false;
|
||||
var amount = 0;
|
||||
|
||||
if (addresses != null) {
|
||||
tx.outs.forEach((out) {
|
||||
try {
|
||||
final p2pkh = bitcoin.P2PKH(
|
||||
data: PaymentData(output: out.script), network: bitcoin.bitcoin);
|
||||
exist = addresses.contains(p2pkh.data.address);
|
||||
|
||||
if (exist) {
|
||||
amount += out.value;
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
final date = timestamp != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000)
|
||||
: DateTime.now();
|
||||
|
||||
return ElectrumTransactionInfo(type,
|
||||
id: tx.getId(),
|
||||
height: height,
|
||||
isPending: false,
|
||||
fee: null,
|
||||
direction: TransactionDirection.incoming,
|
||||
amount: amount,
|
||||
date: date,
|
||||
confirmations: confirmations);
|
||||
}
|
||||
|
||||
factory ElectrumTransactionInfo.fromJson(
|
||||
Map<String, dynamic> data, WalletType type) {
|
||||
return ElectrumTransactionInfo(type,
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int,
|
||||
amount: data['amount'] as int,
|
||||
fee: data['fee'] as int,
|
||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool,
|
||||
confirmations: data['confirmations'] as int);
|
||||
}
|
||||
|
||||
final WalletType type;
|
||||
|
||||
String _fiatAmount;
|
||||
|
||||
@override
|
||||
String amountFormatted() =>
|
||||
'${formatAmount(bitcoinAmountToString(amount: amount))} ${walletTypeToCryptoCurrency(type).title}';
|
||||
|
||||
@override
|
||||
String feeFormatted() => fee != null
|
||||
? '${formatAmount(bitcoinAmountToString(amount: fee))} ${walletTypeToCryptoCurrency(type).title}'
|
||||
: '';
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
ElectrumTransactionInfo updated(ElectrumTransactionInfo info) {
|
||||
return ElectrumTransactionInfo(info.type,
|
||||
id: id,
|
||||
height: info.height,
|
||||
amount: info.amount,
|
||||
fee: info.fee,
|
||||
direction: direction ?? info.direction,
|
||||
date: date ?? info.date,
|
||||
isPending: isPending ?? info.isPending,
|
||||
confirmations: info.confirmations);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final m = <String, dynamic>{};
|
||||
m['id'] = id;
|
||||
m['height'] = height;
|
||||
m['amount'] = amount;
|
||||
m['direction'] = direction.index;
|
||||
m['date'] = date.millisecondsSinceEpoch;
|
||||
m['isPending'] = isPending;
|
||||
m['confirmations'] = confirmations;
|
||||
m['fee'] = fee;
|
||||
return m;
|
||||
}
|
||||
}
|
|
@ -1,548 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:rxdart/subjects.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||
import 'package:cake_wallet/bitcoin/address_to_output_script.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_balance.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_transaction_history.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_no_inputs_exception.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_wrong_balance_exception.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_unspent.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_keys.dart';
|
||||
import 'package:cake_wallet/bitcoin/file.dart';
|
||||
import 'package:cake_wallet/bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cake_wallet/bitcoin/script_hash.dart';
|
||||
import 'package:cake_wallet/bitcoin/utils.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/node.dart';
|
||||
import 'package:cake_wallet/entities/sync_status.dart';
|
||||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum.dart';
|
||||
|
||||
part 'electrum_wallet.g.dart';
|
||||
|
||||
class ElectrumWallet = ElectrumWalletBase with _$ElectrumWallet;
|
||||
|
||||
abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
|
||||
ElectrumTransactionHistory, ElectrumTransactionInfo> with Store {
|
||||
ElectrumWalletBase(
|
||||
{@required String password,
|
||||
@required WalletInfo walletInfo,
|
||||
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
@required List<BitcoinAddressRecord> initialAddresses,
|
||||
@required this.networkType,
|
||||
@required this.mnemonic,
|
||||
ElectrumClient electrumClient,
|
||||
ElectrumBalance initialBalance})
|
||||
: balance = initialBalance ??
|
||||
const ElectrumBalance(confirmed: 0, unconfirmed: 0),
|
||||
hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic),
|
||||
network: networkType)
|
||||
.derivePath("m/0'/0"),
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_feeRates = <int>[],
|
||||
_isTransactionUpdating = false,
|
||||
super(walletInfo) {
|
||||
this.electrumClient = electrumClient ?? ElectrumClient();
|
||||
this.walletInfo = walletInfo;
|
||||
this.unspentCoinsInfo = unspentCoinsInfo;
|
||||
transactionHistory =
|
||||
ElectrumTransactionHistory(walletInfo: walletInfo, password: password);
|
||||
unspentCoins = [];
|
||||
_scripthashesUpdateSubject = {};
|
||||
}
|
||||
|
||||
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
||||
inputsCount * 146 + outputsCounts * 33 + 8;
|
||||
|
||||
final bitcoin.HDWallet hd;
|
||||
final String mnemonic;
|
||||
|
||||
ElectrumClient electrumClient;
|
||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||
|
||||
@override
|
||||
ElectrumWalletAddresses walletAddresses;
|
||||
|
||||
@override
|
||||
@observable
|
||||
ElectrumBalance balance;
|
||||
|
||||
@override
|
||||
@observable
|
||||
SyncStatus syncStatus;
|
||||
|
||||
List<String> get scriptHashes => walletAddresses.addresses
|
||||
.map((addr) => scriptHash(addr.address, networkType: networkType))
|
||||
.toList();
|
||||
|
||||
List<String> get publicScriptHashes => walletAddresses.addresses
|
||||
.where((addr) => !addr.isHidden)
|
||||
.map((addr) => scriptHash(addr.address, networkType: networkType))
|
||||
.toList();
|
||||
|
||||
String get xpub => hd.base58;
|
||||
|
||||
@override
|
||||
String get seed => mnemonic;
|
||||
|
||||
bitcoin.NetworkType networkType;
|
||||
|
||||
@override
|
||||
BitcoinWalletKeys get keys => BitcoinWalletKeys(
|
||||
wif: hd.wif, privateKey: hd.privKey, publicKey: hd.pubKey);
|
||||
|
||||
final String _password;
|
||||
List<BitcoinUnspent> unspentCoins;
|
||||
List<int> _feeRates;
|
||||
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
|
||||
bool _isTransactionUpdating;
|
||||
|
||||
Future<void> init() async {
|
||||
await walletAddresses.init();
|
||||
await transactionHistory.init();
|
||||
await save();
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = StartingSyncStatus();
|
||||
await updateTransactions();
|
||||
_subscribeForUpdates();
|
||||
await _updateBalance();
|
||||
await updateUnspent();
|
||||
_feeRates = await electrumClient.feeRates();
|
||||
|
||||
Timer.periodic(const Duration(minutes: 1),
|
||||
(timer) async => _feeRates = await electrumClient.feeRates());
|
||||
|
||||
syncStatus = SyncedSyncStatus();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> connectToNode({@required Node node}) async {
|
||||
try {
|
||||
syncStatus = ConnectingSyncStatus();
|
||||
await electrumClient.connectToUri(node.uri);
|
||||
electrumClient.onConnectionStatusChange = (bool isConnected) {
|
||||
if (!isConnected) {
|
||||
syncStatus = LostConnectionSyncStatus();
|
||||
}
|
||||
};
|
||||
syncStatus = ConnectedSyncStatus();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingBitcoinTransaction> createTransaction(
|
||||
Object credentials) async {
|
||||
const minAmount = 546;
|
||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||
final inputs = <BitcoinUnspent>[];
|
||||
final outputs = transactionCredentials.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
var allInputsAmount = 0;
|
||||
|
||||
if (unspentCoins.isEmpty) {
|
||||
await updateUnspent();
|
||||
}
|
||||
|
||||
for (final utx in unspentCoins) {
|
||||
if (utx.isSending) {
|
||||
allInputsAmount += utx.value;
|
||||
inputs.add(utx);
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.isEmpty) {
|
||||
throw BitcoinTransactionNoInputsException();
|
||||
}
|
||||
|
||||
final allAmountFee = 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;
|
||||
|
||||
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;
|
||||
|
||||
fee = output.sendAll || amount == allAmount
|
||||
? allAmountFee
|
||||
: calculateEstimatedFee(transactionCredentials.priority, amount);
|
||||
}
|
||||
|
||||
if (fee == 0) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
final totalAmount = amount + fee;
|
||||
|
||||
if (totalAmount > balance.confirmed || totalAmount > allInputsAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
final txb = bitcoin.TransactionBuilder(network: networkType);
|
||||
final changeAddress = walletAddresses.address;
|
||||
var leftAmount = totalAmount;
|
||||
var totalInputAmount = 0;
|
||||
|
||||
inputs.clear();
|
||||
|
||||
for (final utx in unspentCoins) {
|
||||
if (utx.isSending) {
|
||||
leftAmount = leftAmount - utx.value;
|
||||
totalInputAmount += utx.value;
|
||||
inputs.add(utx);
|
||||
|
||||
if (leftAmount <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.isEmpty) {
|
||||
throw BitcoinTransactionNoInputsException();
|
||||
}
|
||||
|
||||
if (amount <= 0 || totalInputAmount < totalAmount) {
|
||||
throw BitcoinTransactionWrongBalanceException(currency);
|
||||
}
|
||||
|
||||
txb.setVersion(1);
|
||||
|
||||
inputs.forEach((input) {
|
||||
if (input.isP2wpkh) {
|
||||
final p2wpkh = bitcoin
|
||||
.P2WPKH(
|
||||
data: generatePaymentData(hd: hd, index: input.address.index),
|
||||
network: networkType)
|
||||
.data;
|
||||
|
||||
txb.addInput(input.hash, input.vout, null, p2wpkh.output);
|
||||
} else {
|
||||
txb.addInput(input.hash, input.vout);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
final feeAmount = feeRate(transactionCredentials.priority) * estimatedSize;
|
||||
final changeValue = totalInputAmount - amount - feeAmount;
|
||||
|
||||
if (changeValue > minAmount) {
|
||||
txb.addOutput(changeAddress, changeValue);
|
||||
}
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
final input = inputs[i];
|
||||
final keyPair = generateKeyPair(
|
||||
hd: hd, index: input.address.index, network: networkType);
|
||||
final witnessValue = input.isP2wpkh ? input.value : null;
|
||||
|
||||
txb.sign(vin: i, keyPair: keyPair, witnessValue: witnessValue);
|
||||
}
|
||||
|
||||
return PendingBitcoinTransaction(txb.build(), type,
|
||||
electrumClient: electrumClient, amount: amount, fee: fee)
|
||||
..addListener((transaction) async {
|
||||
transactionHistory.addOne(transaction);
|
||||
await _updateBalance();
|
||||
});
|
||||
}
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': mnemonic,
|
||||
'account_index': walletAddresses.accountIndex.toString(),
|
||||
'addresses': walletAddresses.addresses.map((addr) => addr.toJSON()).toList(),
|
||||
'balance': balance?.toJSON()
|
||||
});
|
||||
|
||||
int feeRate(TransactionPriority priority) {
|
||||
if (priority is BitcoinTransactionPriority) {
|
||||
return _feeRates[priority.raw];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int feeAmountForPriority(BitcoinTransactionPriority priority, int inputsCount,
|
||||
int outputsCount) =>
|
||||
feeRate(priority) * estimatedTransactionSize(inputsCount, outputsCount);
|
||||
|
||||
@override
|
||||
int calculateEstimatedFee(TransactionPriority priority, int amount,
|
||||
{int outputsCount}) {
|
||||
if (priority is BitcoinTransactionPriority) {
|
||||
int inputsCount = 0;
|
||||
|
||||
if (amount != null) {
|
||||
int totalValue = 0;
|
||||
|
||||
for (final input in unspentCoins) {
|
||||
if (totalValue >= amount) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (input.isSending) {
|
||||
totalValue += input.value;
|
||||
inputsCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalValue < amount) return 0;
|
||||
} else {
|
||||
for (final input in unspentCoins) {
|
||||
if (input.isSending) {
|
||||
inputsCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If send all, then we have no change value
|
||||
final _outputsCount = outputsCount ?? (amount != null ? 2 : 1);
|
||||
|
||||
return feeAmountForPriority(
|
||||
priority, inputsCount, _outputsCount);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save() async {
|
||||
final path = await makePath();
|
||||
await write(path: path, password: _password, data: toJSON());
|
||||
await transactionHistory.save();
|
||||
}
|
||||
|
||||
bitcoin.ECPair keyPairFor({@required int index}) =>
|
||||
generateKeyPair(hd: hd, index: index, network: networkType);
|
||||
|
||||
@override
|
||||
Future<void> rescan({int height}) async => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
await electrumClient?.close();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<String> makePath() async =>
|
||||
pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||
|
||||
Future<void> updateUnspent() async {
|
||||
final unspent = await Future.wait(walletAddresses
|
||||
.addresses.map((address) => electrumClient
|
||||
.getListUnspentWithAddress(address.address, networkType)
|
||||
.then((unspent) => unspent
|
||||
.map((unspent) => BitcoinUnspent.fromJSON(address, unspent)))));
|
||||
unspentCoins = unspent.expand((e) => e).toList();
|
||||
|
||||
if (unspentCoinsInfo.isEmpty) {
|
||||
unspentCoins.forEach((coin) => _addCoinInfo(coin));
|
||||
return;
|
||||
}
|
||||
|
||||
if (unspentCoins.isNotEmpty) {
|
||||
unspentCoins.forEach((coin) {
|
||||
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
||||
element.walletId.contains(id) && element.hash.contains(coin.hash));
|
||||
|
||||
if (coinInfoList.isNotEmpty) {
|
||||
final coinInfo = coinInfoList.first;
|
||||
|
||||
coin.isFrozen = coinInfo.isFrozen;
|
||||
coin.isSending = coinInfo.isSending;
|
||||
coin.note = coinInfo.note;
|
||||
} else {
|
||||
_addCoinInfo(coin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await _refreshUnspentCoinsInfo();
|
||||
}
|
||||
|
||||
Future<void> _addCoinInfo(BitcoinUnspent coin) async {
|
||||
final newInfo = UnspentCoinsInfo(
|
||||
walletId: id,
|
||||
hash: coin.hash,
|
||||
isFrozen: coin.isFrozen,
|
||||
isSending: coin.isSending,
|
||||
note: coin.note
|
||||
);
|
||||
|
||||
await unspentCoinsInfo.add(newInfo);
|
||||
}
|
||||
|
||||
Future<void> _refreshUnspentCoinsInfo() async {
|
||||
try {
|
||||
final List<dynamic> keys = <dynamic>[];
|
||||
final currentWalletUnspentCoins = unspentCoinsInfo.values
|
||||
.where((element) => element.walletId.contains(id));
|
||||
|
||||
if (currentWalletUnspentCoins.isNotEmpty) {
|
||||
currentWalletUnspentCoins.forEach((element) {
|
||||
final existUnspentCoins = unspentCoins
|
||||
?.where((coin) => element.hash.contains(coin?.hash));
|
||||
|
||||
if (existUnspentCoins?.isEmpty ?? true) {
|
||||
keys.add(element.key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (keys.isNotEmpty) {
|
||||
await unspentCoinsInfo.deleteAll(keys);
|
||||
}
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<ElectrumTransactionInfo> fetchTransactionInfo(
|
||||
{@required String hash, @required int height}) async {
|
||||
final tx = await electrumClient.getTransactionExpanded(hash: hash);
|
||||
return ElectrumTransactionInfo.fromElectrumVerbose(tx, walletInfo.type,
|
||||
height: height, addresses: walletAddresses.addresses);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
|
||||
final histories =
|
||||
publicScriptHashes.map((scriptHash) => electrumClient.getHistory(scriptHash));
|
||||
final _historiesWithDetails = await Future.wait(histories)
|
||||
.then((histories) => histories.expand((i) => i).toList())
|
||||
.then((histories) => histories.map((tx) => fetchTransactionInfo(
|
||||
hash: tx['tx_hash'] as String, height: tx['height'] as int)));
|
||||
final historiesWithDetails = await Future.wait(_historiesWithDetails);
|
||||
|
||||
return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>(
|
||||
<String, ElectrumTransactionInfo>{}, (acc, tx) {
|
||||
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
try {
|
||||
if (_isTransactionUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isTransactionUpdating = true;
|
||||
final transactions = await fetchTransactions();
|
||||
transactionHistory.addMany(transactions);
|
||||
await transactionHistory.save();
|
||||
_isTransactionUpdating = false;
|
||||
} catch (e) {
|
||||
print(e);
|
||||
_isTransactionUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _subscribeForUpdates() {
|
||||
scriptHashes.forEach((sh) async {
|
||||
await _scripthashesUpdateSubject[sh]?.close();
|
||||
_scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh);
|
||||
_scripthashesUpdateSubject[sh].listen((event) async {
|
||||
try {
|
||||
await _updateBalance();
|
||||
await updateUnspent();
|
||||
await updateTransactions();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<ElectrumBalance> _fetchBalances() async {
|
||||
final balances = await Future.wait(
|
||||
scriptHashes.map((sh) => electrumClient.getBalance(sh)));
|
||||
final balance = balances.fold(
|
||||
ElectrumBalance(confirmed: 0, unconfirmed: 0),
|
||||
(ElectrumBalance acc, val) => ElectrumBalance(
|
||||
confirmed: (val['confirmed'] as int ?? 0) + (acc.confirmed ?? 0),
|
||||
unconfirmed:
|
||||
(val['unconfirmed'] as int ?? 0) + (acc.unconfirmed ?? 0)));
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
Future<void> _updateBalance() async {
|
||||
balance = await _fetchBalances();
|
||||
await save();
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/entities/wallet_addresses.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'electrum_wallet_addresses.g.dart';
|
||||
|
||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase
|
||||
with _$ElectrumWalletAddresses;
|
||||
|
||||
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
ElectrumWalletAddressesBase(WalletInfo walletInfo,
|
||||
{@required List<BitcoinAddressRecord> initialAddresses,
|
||||
int accountIndex = 0,
|
||||
this.mainHd,
|
||||
this.sideHd})
|
||||
: super(walletInfo) {
|
||||
this.accountIndex = accountIndex;
|
||||
addresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? []).toSet());
|
||||
}
|
||||
|
||||
static const regularAddressesCount = 22;
|
||||
static const hiddenAddressesCount = 17;
|
||||
|
||||
@override
|
||||
@observable
|
||||
String address;
|
||||
|
||||
bitcoin.HDWallet mainHd;
|
||||
bitcoin.HDWallet sideHd;
|
||||
|
||||
ObservableList<BitcoinAddressRecord> addresses;
|
||||
|
||||
int accountIndex;
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
await generateAddresses();
|
||||
address = addresses[accountIndex].address;
|
||||
await updateAddressesInBox();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> nextAddress() async {
|
||||
accountIndex += 1;
|
||||
|
||||
if (accountIndex >= addresses.length) {
|
||||
accountIndex = 0;
|
||||
}
|
||||
|
||||
address = addresses[accountIndex].address;
|
||||
|
||||
await updateAddressesInBox();
|
||||
}
|
||||
|
||||
Future<void> generateAddresses() async {
|
||||
final regularAddresses = <BitcoinAddressRecord>[];
|
||||
final hiddenAddresses = <BitcoinAddressRecord>[];
|
||||
|
||||
addresses.forEach((addr) {
|
||||
if (addr.isHidden) {
|
||||
hiddenAddresses.add(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
regularAddresses.add(addr);
|
||||
});
|
||||
|
||||
if (regularAddresses.length < regularAddressesCount) {
|
||||
final addressesCount = regularAddressesCount - regularAddresses.length;
|
||||
await generateNewAddresses(addressesCount,
|
||||
startIndex: regularAddresses.length, hd: mainHd, isHidden: false);
|
||||
}
|
||||
|
||||
if (hiddenAddresses.length < hiddenAddressesCount) {
|
||||
final addressesCount = hiddenAddressesCount - hiddenAddresses.length;
|
||||
await generateNewAddresses(addressesCount,
|
||||
startIndex: hiddenAddresses.length, hd: sideHd, isHidden: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<BitcoinAddressRecord> generateNewAddress(
|
||||
{bool isHidden = false, bitcoin.HDWallet hd}) async {
|
||||
accountIndex += 1;
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(index: accountIndex, hd: hd),
|
||||
index: accountIndex,
|
||||
isHidden: isHidden);
|
||||
addresses.add(address);
|
||||
return address;
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> generateNewAddresses(int count,
|
||||
{int startIndex = 0, bitcoin.HDWallet hd, bool isHidden = false}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
final address = BitcoinAddressRecord(getAddress(index: i, hd: hd),
|
||||
index: i, isHidden: isHidden);
|
||||
list.add(address);
|
||||
}
|
||||
|
||||
addresses.addAll(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*Future<void> updateAddress(String address) async {
|
||||
for (final addr in addresses) {
|
||||
if (addr.address == address) {
|
||||
await save();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
String getAddress({@required int index, @required bitcoin.HDWallet hd}) => '';
|
||||
|
||||
@override
|
||||
Future<void> updateAddressesInBox() async {
|
||||
try {
|
||||
addressesMap.clear();
|
||||
addressesMap[address] = '';
|
||||
|
||||
await saveAddressesInBox();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_balance.dart';
|
||||
import 'package:cake_wallet/bitcoin/file.dart';
|
||||
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
class ElectrumWallletSnapshot {
|
||||
ElectrumWallletSnapshot(this.name, this.type, this.password);
|
||||
|
||||
final String name;
|
||||
final String password;
|
||||
final WalletType type;
|
||||
|
||||
String mnemonic;
|
||||
List<BitcoinAddressRecord> addresses;
|
||||
ElectrumBalance balance;
|
||||
int accountIndex;
|
||||
|
||||
Future<void> load() async {
|
||||
try {
|
||||
final path = await pathForWallet(name: name, type: type);
|
||||
final jsonSource = await read(path: path, password: password);
|
||||
final data = json.decode(jsonSource) as Map;
|
||||
final addressesTmp = data['addresses'] as List ?? <Object>[];
|
||||
mnemonic = data['mnemonic'] as String;
|
||||
addresses = addressesTmp
|
||||
.whereType<String>()
|
||||
.map((addr) => BitcoinAddressRecord.fromJSON(addr))
|
||||
.toList();
|
||||
balance = ElectrumBalance.fromJSON(data['balance'] as String) ??
|
||||
ElectrumBalance(confirmed: 0, unconfirmed: 0);
|
||||
accountIndex = 0;
|
||||
|
||||
try {
|
||||
accountIndex = int.parse(data['account_index'] as String);
|
||||
} catch (_) {}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import 'dart:io';
|
||||
import 'package:cake_wallet/bitcoin/key.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
Future<void> write(
|
||||
{@required String path,
|
||||
@required String password,
|
||||
@required String data}) async {
|
||||
final keys = extractKeys(password);
|
||||
final key = encrypt.Key.fromBase64(keys.first);
|
||||
final iv = encrypt.IV.fromBase64(keys.last);
|
||||
final encrypted = await encode(key: key, iv: iv, data: data);
|
||||
final f = File(path);
|
||||
f.writeAsStringSync(encrypted);
|
||||
}
|
||||
|
||||
Future<void> writeData(
|
||||
{@required String path,
|
||||
@required String password,
|
||||
@required String data}) async {
|
||||
final keys = extractKeys(password);
|
||||
final key = encrypt.Key.fromBase64(keys.first);
|
||||
final iv = encrypt.IV.fromBase64(keys.last);
|
||||
final encrypted = await encode(key: key, iv: iv, data: data);
|
||||
final f = File(path);
|
||||
f.writeAsStringSync(encrypted);
|
||||
}
|
||||
|
||||
Future<String> read({@required String path, @required String password}) async {
|
||||
final file = File(path);
|
||||
|
||||
if (!file.existsSync()) {
|
||||
file.createSync();
|
||||
}
|
||||
|
||||
final encrypted = file.readAsStringSync();
|
||||
|
||||
return decode(password: password, data: encrypted);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
|
||||
const ivEncodedStringLength = 12;
|
||||
|
||||
String generateKey() {
|
||||
final key = encrypt.Key.fromSecureRandom(512);
|
||||
final iv = encrypt.IV.fromSecureRandom(8);
|
||||
|
||||
return key.base64 + iv.base64;
|
||||
}
|
||||
|
||||
List<String> extractKeys(String key) {
|
||||
final _key = key.substring(0, key.length - ivEncodedStringLength);
|
||||
final iv = key.substring(key.length - ivEncodedStringLength);
|
||||
|
||||
return [_key, iv];
|
||||
}
|
||||
|
||||
Future<String> encode({encrypt.Key key, encrypt.IV iv, String data}) async {
|
||||
final encrypter = encrypt.Encrypter(encrypt.Salsa20(key));
|
||||
final encrypted = encrypter.encrypt(data, iv: iv);
|
||||
|
||||
return encrypted.base64;
|
||||
}
|
||||
|
||||
Future<String> decode({String password, String data}) async {
|
||||
final keys = extractKeys(password);
|
||||
final key = encrypt.Key.fromBase64(keys.first);
|
||||
final iv = encrypt.IV.fromBase64(keys.last);
|
||||
final encrypter = encrypt.Encrypter(encrypt.Salsa20(key));
|
||||
final encrypted = encrypter.decrypt64(data, iv: iv);
|
||||
|
||||
return encrypted;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||
|
||||
final litecoinNetwork = NetworkType(
|
||||
messagePrefix: '\x19Litecoin Signed Message:\n',
|
||||
bech32: 'ltc',
|
||||
bip32: Bip32Type(public: 0x0488b21e, private: 0x0488ade4),
|
||||
pubKeyHash: 0x30,
|
||||
scriptHash: 0x32,
|
||||
wif: 0xb0);
|
|
@ -1,82 +0,0 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
|
||||
import 'package:cake_wallet/bitcoin/litecoin_wallet_addresses.dart';
|
||||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet_snapshot.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_balance.dart';
|
||||
import 'package:cake_wallet/bitcoin/litecoin_network.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
|
||||
part 'litecoin_wallet.g.dart';
|
||||
|
||||
class LitecoinWallet = LitecoinWalletBase with _$LitecoinWallet;
|
||||
|
||||
abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
LitecoinWalletBase(
|
||||
{@required String mnemonic,
|
||||
@required String password,
|
||||
@required WalletInfo walletInfo,
|
||||
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
List<BitcoinAddressRecord> initialAddresses,
|
||||
ElectrumBalance initialBalance,
|
||||
int accountIndex = 0})
|
||||
: super(
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
networkType: litecoinNetwork,
|
||||
initialAddresses: initialAddresses,
|
||||
initialBalance: initialBalance) {
|
||||
walletAddresses = LitecoinWalletAddresses(
|
||||
walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
accountIndex: accountIndex,
|
||||
mainHd: hd,
|
||||
sideHd: bitcoin.HDWallet
|
||||
.fromSeed(mnemonicToSeedBytes(mnemonic), network: networkType)
|
||||
.derivePath("m/0'/1"),
|
||||
networkType: networkType,);
|
||||
}
|
||||
|
||||
static Future<LitecoinWallet> open({
|
||||
@required String name,
|
||||
@required WalletInfo walletInfo,
|
||||
@required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
@required String password,
|
||||
}) async {
|
||||
final snp = ElectrumWallletSnapshot(name, walletInfo.type, password);
|
||||
await snp.load();
|
||||
return LitecoinWallet(
|
||||
mnemonic: snp.mnemonic,
|
||||
password: password,
|
||||
walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp.addresses,
|
||||
initialBalance: snp.balance,
|
||||
accountIndex: snp.accountIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
int feeRate(TransactionPriority priority) {
|
||||
if (priority is LitecoinTransactionPriority) {
|
||||
switch (priority) {
|
||||
case LitecoinTransactionPriority.slow:
|
||||
return 1;
|
||||
case LitecoinTransactionPriority.medium:
|
||||
return 2;
|
||||
case LitecoinTransactionPriority.fast:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cake_wallet/bitcoin/utils.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'litecoin_wallet_addresses.g.dart';
|
||||
|
||||
class LitecoinWalletAddresses = LitecoinWalletAddressesBase
|
||||
with _$LitecoinWalletAddresses;
|
||||
|
||||
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
|
||||
with Store {
|
||||
LitecoinWalletAddressesBase(
|
||||
WalletInfo walletInfo,
|
||||
{@required List<BitcoinAddressRecord> initialAddresses,
|
||||
int accountIndex = 0,
|
||||
@required bitcoin.HDWallet mainHd,
|
||||
@required bitcoin.HDWallet sideHd,
|
||||
@required this.networkType})
|
||||
: super(
|
||||
walletInfo,
|
||||
initialAddresses: initialAddresses,
|
||||
accountIndex: accountIndex,
|
||||
mainHd: mainHd,
|
||||
sideHd: sideHd);
|
||||
|
||||
bitcoin.NetworkType networkType;
|
||||
|
||||
|
||||
@override
|
||||
String getAddress({@required int index, @required bitcoin.HDWallet hd}) =>
|
||||
generateP2WPKHAddress(hd: hd, index: index, networkType: networkType);
|
||||
|
||||
@override
|
||||
Future<void> generateAddresses() async {
|
||||
if (addresses.length < 33) {
|
||||
final addressesCount = 22 - addresses.length;
|
||||
await generateNewAddresses(addressesCount,
|
||||
hd: mainHd, startIndex: addresses.length);
|
||||
await generateNewAddresses(11,
|
||||
startIndex: 0, hd: sideHd, isHidden: true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
import 'dart:io';
|
||||
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
|
||||
import 'package:cake_wallet/bitcoin/litecoin_wallet.dart';
|
||||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
|
||||
class LitecoinWalletService extends WalletService<
|
||||
BitcoinNewWalletCredentials,
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinRestoreWalletFromWIFCredentials> {
|
||||
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
||||
|
||||
@override
|
||||
WalletType getType() => WalletType.litecoin;
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
||||
final wallet = LitecoinWallet(
|
||||
mnemonic: await generateMnemonic(),
|
||||
password: credentials.password,
|
||||
walletInfo: credentials.walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isWalletExit(String name) async =>
|
||||
File(await pathForWallet(name: name, type: getType())).existsSync();
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> openWallet(String name, String password) async {
|
||||
final walletInfo = walletInfoSource.values.firstWhere(
|
||||
(info) => info.id == WalletBase.idFor(name, getType()),
|
||||
orElse: () => null);
|
||||
final wallet = await LitecoinWalletBase.open(
|
||||
password: password, name: name, walletInfo: walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> remove(String wallet) async =>
|
||||
File(await pathForWalletDir(name: wallet, type: getType()))
|
||||
.delete(recursive: true);
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromKeys(
|
||||
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromSeed(
|
||||
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||
if (!validateMnemonic(credentials.mnemonic)) {
|
||||
throw BitcoinMnemonicIsIncorrectException();
|
||||
}
|
||||
|
||||
final wallet = LitecoinWallet(
|
||||
password: credentials.password,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_commit_transaction_exception.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/core/pending_transaction.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cake_wallet/entities/transaction_direction.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
class PendingBitcoinTransaction with PendingTransaction {
|
||||
PendingBitcoinTransaction(this._tx, this.type,
|
||||
{@required this.electrumClient,
|
||||
@required this.amount,
|
||||
@required this.fee})
|
||||
: _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
|
||||
|
||||
final WalletType type;
|
||||
final bitcoin.Transaction _tx;
|
||||
final ElectrumClient electrumClient;
|
||||
final int amount;
|
||||
final int fee;
|
||||
|
||||
@override
|
||||
String get id => _tx.getId();
|
||||
|
||||
@override
|
||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => bitcoinAmountToString(amount: fee);
|
||||
|
||||
final List<void Function(ElectrumTransactionInfo transaction)> _listeners;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
final result =
|
||||
await electrumClient.broadcastTransaction(transactionRaw: _tx.toHex());
|
||||
|
||||
if (result.isEmpty) {
|
||||
throw BitcoinCommitTransactionException();
|
||||
}
|
||||
|
||||
_listeners?.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
||||
void addListener(
|
||||
void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
confirmations: 0,
|
||||
fee: fee);
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
String scriptHash(String address, {@required bitcoin.NetworkType networkType}) {
|
||||
final outputScript =
|
||||
bitcoin.Address.addressToOutputScript(address, networkType);
|
||||
final parts = sha256.convert(outputScript).toString().split('');
|
||||
var res = '';
|
||||
|
||||
for (var i = parts.length - 1; i >= 0; i--) {
|
||||
final char = parts[i];
|
||||
i--;
|
||||
final nextChar = parts[i];
|
||||
res += nextChar;
|
||||
res += char;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'unspent_coins_info.g.dart';
|
||||
|
||||
@HiveType(typeId: UnspentCoinsInfo.typeId)
|
||||
class UnspentCoinsInfo extends HiveObject {
|
||||
UnspentCoinsInfo({
|
||||
this.walletId,
|
||||
this.hash,
|
||||
this.isFrozen,
|
||||
this.isSending,
|
||||
this.note});
|
||||
|
||||
static const typeId = 9;
|
||||
static const boxName = 'Unspent';
|
||||
static const boxKey = 'unspentBoxKey';
|
||||
|
||||
@HiveField(0)
|
||||
String walletId;
|
||||
|
||||
@HiveField(1)
|
||||
String hash;
|
||||
|
||||
@HiveField(2)
|
||||
bool isFrozen;
|
||||
|
||||
@HiveField(3)
|
||||
bool isSending;
|
||||
|
||||
@HiveField(4)
|
||||
String note;
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||
import 'package:hex/hex.dart';
|
||||
|
||||
bitcoin.PaymentData generatePaymentData(
|
||||
{@required bitcoin.HDWallet hd, @required int index}) =>
|
||||
PaymentData(
|
||||
pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey)));
|
||||
|
||||
bitcoin.ECPair generateKeyPair(
|
||||
{@required bitcoin.HDWallet hd,
|
||||
@required int index,
|
||||
bitcoin.NetworkType network}) =>
|
||||
bitcoin.ECPair.fromWIF(hd.derive(index).wif, network: network);
|
||||
|
||||
String generateP2WPKHAddress(
|
||||
{@required bitcoin.HDWallet hd,
|
||||
@required int index,
|
||||
bitcoin.NetworkType networkType}) =>
|
||||
bitcoin
|
||||
.P2WPKH(
|
||||
data: PaymentData(
|
||||
pubkey:
|
||||
Uint8List.fromList(HEX.decode(hd.derive(index).pubKey))),
|
||||
network: networkType)
|
||||
.data
|
||||
.address;
|
||||
|
||||
String generateP2WPKHAddressByPath(
|
||||
{@required bitcoin.HDWallet hd,
|
||||
@required String path,
|
||||
bitcoin.NetworkType networkType}) =>
|
||||
bitcoin
|
||||
.P2WPKH(
|
||||
data: PaymentData(
|
||||
pubkey:
|
||||
Uint8List.fromList(HEX.decode(hd.derivePath(path).pubKey))),
|
||||
network: networkType)
|
||||
.data
|
||||
.address;
|
||||
|
||||
String generateP2PKHAddress(
|
||||
{@required bitcoin.HDWallet hd,
|
||||
@required int index,
|
||||
bitcoin.NetworkType networkType}) =>
|
||||
bitcoin
|
||||
.P2PKH(
|
||||
data: PaymentData(
|
||||
pubkey:
|
||||
Uint8List.fromList(HEX.decode(hd.derive(index).pubKey))),
|
||||
network: networkType)
|
||||
.data
|
||||
.address;
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
abstract class BuyProvider {
|
||||
BuyProvider({this.wallet, this.isTestEnvironment});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/entities/enumerable_item.dart';
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class BuyProviderDescription extends EnumerableItem<int>
|
||||
with Serializable<int> {
|
||||
|
|
|
@ -6,11 +6,11 @@ import 'package:cake_wallet/buy/buy_amount.dart';
|
|||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class MoonPaySellProvider {
|
||||
MoonPaySellProvider({this.isTest = false})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/entities/format_amount.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
|
||||
part 'order.g.dart';
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ import 'package:cake_wallet/buy/buy_amount.dart';
|
|||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class AddressLabelValidator extends TextValidator {
|
||||
AddressLabelValidator({WalletType type})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class AddressValidator extends TextValidator {
|
||||
AddressValidator({@required CryptoCurrency type})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class AmountConverter {
|
||||
static const _moneroAmountLength = 12;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class AmountValidator extends TextValidator {
|
||||
AmountValidator({WalletType type, bool isAutovalidate = false})
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'package:cake_wallet/core/key_service.dart';
|
|||
import 'package:cake_wallet/entities/encrypt.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/secret_store_key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
class BackupService {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:uuid/uuid.dart';
|
||||
import 'package:cake_wallet/bitcoin/key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/key.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
String generateWalletPassword(WalletType type) {
|
||||
switch (type) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
const bitcoinMnemonicLength = 12;
|
||||
const moneroMnemonicLength = 25;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class MoneroLabelValidator extends TextValidator {
|
||||
MoneroLabelValidator({@required CryptoCurrency type})
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
mixin PendingTransaction {
|
||||
String get id;
|
||||
String get amountFormatted;
|
||||
String get feeFormatted;
|
||||
|
||||
Future<void> commit();
|
||||
}
|
|
@ -1,15 +1,8 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart' as bitcoin_electrum;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/chinese_simplified.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/dutch.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/english.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/german.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/japanese.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/portuguese.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/russian.dart';
|
||||
import 'package:cake_wallet/monero/mnemonics/spanish.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/utils/language_list.dart';
|
||||
|
||||
class SeedValidator extends Validator<MnemonicItem> {
|
||||
|
@ -27,46 +20,15 @@ class SeedValidator extends Validator<MnemonicItem> {
|
|||
case WalletType.litecoin:
|
||||
return getBitcoinWordList(language);
|
||||
case WalletType.monero:
|
||||
return getMoneroWordList(language);
|
||||
return monero.getMoneroWordList(language);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> getMoneroWordList(String language) {
|
||||
switch (language) {
|
||||
case LanguageList.english:
|
||||
return EnglishMnemonics.words;
|
||||
break;
|
||||
case LanguageList.chineseSimplified:
|
||||
return ChineseSimplifiedMnemonics.words;
|
||||
break;
|
||||
case LanguageList.dutch:
|
||||
return DutchMnemonics.words;
|
||||
break;
|
||||
case LanguageList.german:
|
||||
return GermanMnemonics.words;
|
||||
break;
|
||||
case LanguageList.japanese:
|
||||
return JapaneseMnemonics.words;
|
||||
break;
|
||||
case LanguageList.portuguese:
|
||||
return PortugueseMnemonics.words;
|
||||
break;
|
||||
case LanguageList.russian:
|
||||
return RussianMnemonics.words;
|
||||
break;
|
||||
case LanguageList.spanish:
|
||||
return SpanishMnemonics.words;
|
||||
break;
|
||||
default:
|
||||
return EnglishMnemonics.words;
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> getBitcoinWordList(String language) {
|
||||
assert(language.toLowerCase() == LanguageList.english.toLowerCase());
|
||||
return bitcoin_electrum.englishWordlist;
|
||||
return bitcoin.getWordList();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||
|
||||
abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
|
||||
TransactionHistoryBase();
|
||||
// : _isUpdating = false;
|
||||
|
||||
@observable
|
||||
ObservableMap<String, TransactionType> transactions;
|
||||
|
||||
Future<void> save();
|
||||
|
||||
void addOne(TransactionType transaction);
|
||||
|
||||
void addMany(Map<String, TransactionType> transactions);
|
||||
|
||||
// bool _isUpdating;
|
||||
|
||||
// @action
|
||||
// Future<void> update() async {
|
||||
// if (_isUpdating) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// try {
|
||||
// _isUpdating = true;
|
||||
// final _transactions = await fetchTransactions();
|
||||
// transactions.keys
|
||||
// .toSet()
|
||||
// .difference(_transactions.keys.toSet())
|
||||
// .forEach((k) => transactions.remove(k));
|
||||
// _transactions.forEach((key, value) => transactions[key] = value);
|
||||
// _isUpdating = false;
|
||||
// } catch (e) {
|
||||
// _isUpdating = false;
|
||||
// rethrow;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void updateAsync({void Function() onFinished}) {
|
||||
// fetchTransactionsAsync(
|
||||
// (transaction) => transactions[transaction.id] = transaction,
|
||||
// onFinished: onFinished);
|
||||
// }
|
||||
|
||||
// void fetchTransactionsAsync(
|
||||
// void Function(TransactionType transaction) onTransactionLoaded,
|
||||
// {void Function() onFinished});
|
||||
|
||||
// Future<Map<String, TransactionType>> fetchTransactions();
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
import 'package:cake_wallet/entities/balance.dart';
|
||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
import 'package:cake_wallet/entities/wallet_addresses.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cake_wallet/core/pending_transaction.dart';
|
||||
import 'package:cake_wallet/core/transaction_history.dart';
|
||||
import 'package:cake_wallet/entities/currency_for_wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/sync_status.dart';
|
||||
import 'package:cake_wallet/entities/node.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
abstract class WalletBase<
|
||||
BalanceType extends Balance,
|
||||
HistoryType extends TransactionHistoryBase,
|
||||
TransactionType extends TransactionInfo> {
|
||||
WalletBase(this.walletInfo);
|
||||
|
||||
static String idFor(String name, WalletType type) =>
|
||||
walletTypeToString(type).toLowerCase() + '_' + name;
|
||||
|
||||
WalletInfo walletInfo;
|
||||
|
||||
WalletType get type => walletInfo.type;
|
||||
|
||||
CryptoCurrency get currency => currencyForWalletType(type);
|
||||
|
||||
String get id => walletInfo.id;
|
||||
|
||||
String get name => walletInfo.name;
|
||||
|
||||
//String get address;
|
||||
|
||||
//set address(String address);
|
||||
|
||||
BalanceType get balance;
|
||||
|
||||
SyncStatus get syncStatus;
|
||||
|
||||
set syncStatus(SyncStatus status);
|
||||
|
||||
String get seed;
|
||||
|
||||
Object get keys;
|
||||
|
||||
WalletAddresses get walletAddresses;
|
||||
|
||||
HistoryType transactionHistory;
|
||||
|
||||
Future<void> connectToNode({@required Node node});
|
||||
|
||||
Future<void> startSync();
|
||||
|
||||
Future<PendingTransaction> createTransaction(Object credentials);
|
||||
|
||||
int calculateEstimatedFee(TransactionPriority priority, int amount);
|
||||
|
||||
// void fetchTransactionsAsync(
|
||||
// void Function(TransactionType transaction) onTransactionLoaded,
|
||||
// {void Function() onFinished});
|
||||
|
||||
Future<Map<String, TransactionType>> fetchTransactions();
|
||||
|
||||
Future<void> save();
|
||||
|
||||
Future<void> rescan({int height});
|
||||
|
||||
void close();
|
||||
}
|
|
@ -3,11 +3,11 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/generate_wallet_password.dart';
|
||||
import 'package:cake_wallet/core/wallet_credentials.dart';
|
||||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class WalletCreationService {
|
||||
WalletCreationService(
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
|
||||
abstract class WalletCredentials {
|
||||
WalletCredentials({this.name, this.password, this.height, this.walletInfo});
|
||||
|
||||
final String name;
|
||||
final int height;
|
||||
String password;
|
||||
WalletInfo walletInfo;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/wallet_credentials.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
abstract class WalletService<N extends WalletCredentials,
|
||||
RFS extends WalletCredentials, RFK extends WalletCredentials> {
|
||||
WalletType getType();
|
||||
|
||||
Future<WalletBase> create(N credentials);
|
||||
|
||||
Future<WalletBase> restoreFromSeed(RFS credentials);
|
||||
|
||||
Future<WalletBase> restoreFromKeys(RFK credentials);
|
||||
|
||||
Future<WalletBase> openWallet(String name, String password);
|
||||
|
||||
Future<bool> isWalletExit(String name);
|
||||
|
||||
Future<void> remove(String wallet);
|
||||
}
|
36
lib/di.dart
36
lib/di.dart
|
@ -1,16 +1,15 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
|
||||
import 'package:cake_wallet/bitcoin/litecoin_wallet_service.dart';
|
||||
import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cake_wallet/core/backup_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||
import 'package:cake_wallet/monero/monero_wallet_service.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/node.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
|
||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||
|
@ -51,8 +50,7 @@ import 'package:cake_wallet/store/secret_store.dart';
|
|||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/monero/monero_wallet.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
|
@ -108,7 +106,7 @@ import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
|
|||
import 'package:cake_wallet/view_model/wallet_restoration_from_keys_vm.dart';
|
||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
import 'package:cake_wallet/store/authentication_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
|
@ -354,7 +352,7 @@ Future setup(
|
|||
getIt.registerFactory(() {
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
|
||||
if (wallet is MoneroWallet) {
|
||||
if (wallet.type == WalletType.monero) {
|
||||
return MoneroAccountListViewModel(wallet);
|
||||
}
|
||||
|
||||
|
@ -383,7 +381,7 @@ Future setup(
|
|||
getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel,
|
||||
AccountListItem, void>(
|
||||
(AccountListItem account, _) => MoneroAccountEditOrCreateViewModel(
|
||||
(getIt.get<AppStore>().wallet as MoneroWallet).walletAddresses.accountList,
|
||||
monero.getAccountList(getIt.get<AppStore>().wallet),
|
||||
wallet: getIt.get<AppStore>().wallet,
|
||||
accountListItem: account));
|
||||
|
||||
|
@ -467,23 +465,15 @@ Future setup(
|
|||
getIt.registerFactory(
|
||||
() => ExchangeTemplatePage(getIt.get<ExchangeViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => MoneroWalletService(_walletInfoSource));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
BitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
LitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource));
|
||||
|
||||
getIt.registerFactoryParam<WalletService, WalletType, void>(
|
||||
(WalletType param1, __) {
|
||||
switch (param1) {
|
||||
case WalletType.monero:
|
||||
return getIt.get<MoneroWalletService>();
|
||||
return monero.createMoneroWalletService(_walletInfoSource);
|
||||
case WalletType.bitcoin:
|
||||
return getIt.get<BitcoinWalletService>();
|
||||
return bitcoin.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
case WalletType.litecoin:
|
||||
return getIt.get<LitecoinWalletService>();
|
||||
return bitcoin.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
abstract class Balance {
|
||||
const Balance(this.available, this.additional);
|
||||
|
||||
final int available;
|
||||
|
||||
final int additional;
|
||||
|
||||
String get formattedAvailableBalance;
|
||||
|
||||
String get formattedAdditionalBalance;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/entities/enumerable_item.dart';
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> {
|
||||
const BalanceDisplayMode({@required String title, @required int raw})
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import 'package:cake_wallet/entities/monero_transaction_priority.dart';
|
||||
|
||||
double calculateEstimatedFee({MoneroTransactionPriority priority}) {
|
||||
if (priority == MoneroTransactionPriority.slow) {
|
||||
return 0.00002459;
|
||||
}
|
||||
|
||||
if (priority == MoneroTransactionPriority.regular) {
|
||||
return 0.00012305;
|
||||
}
|
||||
|
||||
if (priority == MoneroTransactionPriority.medium) {
|
||||
return 0.00024503;
|
||||
}
|
||||
|
||||
if (priority == MoneroTransactionPriority.fast) {
|
||||
return 0.00061453;
|
||||
}
|
||||
|
||||
if (priority == MoneroTransactionPriority.fastest) {
|
||||
return 0.0260216;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cw_core/keyable.dart';
|
||||
|
||||
part 'contact.g.dart';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
abstract class ContactBase {
|
||||
String name;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// import 'package:hive/hive.dart';
|
||||
// import 'package:mobx/mobx.dart';
|
||||
// import 'package:cake_wallet/entities/contact.dart';
|
||||
// import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
// import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
// part 'contact_model.g.dart';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/record.dart';
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
double cryptoAmountToDouble({num amount, num divider}) => amount / divider;
|
|
@ -1,125 +0,0 @@
|
|||
import 'package:cake_wallet/entities/enumerable_item.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'crypto_currency.g.dart';
|
||||
|
||||
@HiveType(typeId: 0)
|
||||
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
|
||||
const CryptoCurrency({final String title, final int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const all = [
|
||||
CryptoCurrency.xmr,
|
||||
CryptoCurrency.ada,
|
||||
CryptoCurrency.bch,
|
||||
CryptoCurrency.bnb,
|
||||
CryptoCurrency.btc,
|
||||
CryptoCurrency.dai,
|
||||
CryptoCurrency.dash,
|
||||
CryptoCurrency.eos,
|
||||
CryptoCurrency.eth,
|
||||
CryptoCurrency.ltc,
|
||||
CryptoCurrency.trx,
|
||||
CryptoCurrency.usdt,
|
||||
CryptoCurrency.usdterc20,
|
||||
CryptoCurrency.xlm,
|
||||
CryptoCurrency.xrp
|
||||
];
|
||||
static const xmr = CryptoCurrency(title: 'XMR', raw: 0);
|
||||
static const ada = CryptoCurrency(title: 'ADA', raw: 1);
|
||||
static const bch = CryptoCurrency(title: 'BCH', raw: 2);
|
||||
static const bnb = CryptoCurrency(title: 'BNB BEP2', raw: 3);
|
||||
static const btc = CryptoCurrency(title: 'BTC', raw: 4);
|
||||
static const dai = CryptoCurrency(title: 'DAI', raw: 5);
|
||||
static const dash = CryptoCurrency(title: 'DASH', raw: 6);
|
||||
static const eos = CryptoCurrency(title: 'EOS', raw: 7);
|
||||
static const eth = CryptoCurrency(title: 'ETH', raw: 8);
|
||||
static const ltc = CryptoCurrency(title: 'LTC', raw: 9);
|
||||
static const nano = CryptoCurrency(title: 'NANO', raw: 10);
|
||||
static const trx = CryptoCurrency(title: 'TRX', raw: 11);
|
||||
static const usdt = CryptoCurrency(title: 'USDT', raw: 12);
|
||||
static const usdterc20 = CryptoCurrency(title: 'USDTERC20', raw: 13);
|
||||
static const xlm = CryptoCurrency(title: 'XLM', raw: 14);
|
||||
static const xrp = CryptoCurrency(title: 'XRP', raw: 15);
|
||||
|
||||
static CryptoCurrency deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return CryptoCurrency.xmr;
|
||||
case 1:
|
||||
return CryptoCurrency.ada;
|
||||
case 2:
|
||||
return CryptoCurrency.bch;
|
||||
case 3:
|
||||
return CryptoCurrency.bnb;
|
||||
case 4:
|
||||
return CryptoCurrency.btc;
|
||||
case 5:
|
||||
return CryptoCurrency.dai;
|
||||
case 6:
|
||||
return CryptoCurrency.dash;
|
||||
case 7:
|
||||
return CryptoCurrency.eos;
|
||||
case 8:
|
||||
return CryptoCurrency.eth;
|
||||
case 9:
|
||||
return CryptoCurrency.ltc;
|
||||
case 10:
|
||||
return CryptoCurrency.nano;
|
||||
case 11:
|
||||
return CryptoCurrency.trx;
|
||||
case 12:
|
||||
return CryptoCurrency.usdt;
|
||||
case 13:
|
||||
return CryptoCurrency.usdterc20;
|
||||
case 14:
|
||||
return CryptoCurrency.xlm;
|
||||
case 15:
|
||||
return CryptoCurrency.xrp;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static CryptoCurrency fromString(String raw) {
|
||||
switch (raw.toLowerCase()) {
|
||||
case 'xmr':
|
||||
return CryptoCurrency.xmr;
|
||||
case 'ada':
|
||||
return CryptoCurrency.ada;
|
||||
case 'bch':
|
||||
return CryptoCurrency.bch;
|
||||
case 'bnbmainnet':
|
||||
return CryptoCurrency.bnb;
|
||||
case 'btc':
|
||||
return CryptoCurrency.btc;
|
||||
case 'dai':
|
||||
return CryptoCurrency.dai;
|
||||
case 'dash':
|
||||
return CryptoCurrency.dash;
|
||||
case 'eos':
|
||||
return CryptoCurrency.eos;
|
||||
case 'eth':
|
||||
return CryptoCurrency.eth;
|
||||
case 'ltc':
|
||||
return CryptoCurrency.ltc;
|
||||
case 'nano':
|
||||
return CryptoCurrency.nano;
|
||||
case 'trx':
|
||||
return CryptoCurrency.trx;
|
||||
case 'usdt':
|
||||
return CryptoCurrency.usdt;
|
||||
case 'usdterc20':
|
||||
return CryptoCurrency.usdterc20;
|
||||
case 'xlm':
|
||||
return CryptoCurrency.xlm;
|
||||
case 'xrp':
|
||||
return CryptoCurrency.xrp;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
CryptoCurrency currencyForWalletType(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return CryptoCurrency.btc;
|
||||
case WalletType.monero:
|
||||
return CryptoCurrency.xmr;
|
||||
case WalletType.litecoin:
|
||||
return CryptoCurrency.ltc;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
import 'dart:io' show File, Platform;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cake_wallet/entities/secret_store_key.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/node_list.dart';
|
||||
import 'package:cake_wallet/entities/monero_transaction_priority.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/fs_migration.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:encrypt/encrypt.dart' as encrypt;
|
||||
|
||||
|
@ -55,7 +55,7 @@ Future defaultSettingsMigration(
|
|||
FiatCurrency.usd.toString());
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentTransactionPriorityKeyLegacy,
|
||||
MoneroTransactionPriority.standard.raw);
|
||||
monero.getDefaultTransactionPriority().raw);
|
||||
await sharedPreferences.setInt(
|
||||
PreferencesKey.currentBalanceDisplayModeKey,
|
||||
BalanceDisplayMode.availableBalance.raw);
|
||||
|
@ -308,7 +308,7 @@ Future<void> changeTransactionPriorityAndFeeRateKeys(
|
|||
await sharedPreferences.setInt(
|
||||
PreferencesKey.moneroTransactionPriority, legacyTransactionPriority);
|
||||
await sharedPreferences.setInt(PreferencesKey.bitcoinTransactionPriority,
|
||||
BitcoinTransactionPriority.medium.serialize());
|
||||
bitcoin.getMediumTransactionPriority().serialize());
|
||||
}
|
||||
|
||||
Future<void> changeDefaultMoneroNode(
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class EnumerableItem<T> {
|
||||
const EnumerableItem({@required this.title, @required this.raw});
|
||||
|
||||
final T raw;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
}
|
||||
|
||||
mixin Serializable<T> on EnumerableItem<T> {
|
||||
static Serializable deserialize<T>({T raw}) => null;
|
||||
T serialize() => raw;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/entities/enumerable_item.dart';
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
|
||||
const FiatCurrency({String symbol}) : super(title: symbol, raw: symbol);
|
||||
|
|
|
@ -6,15 +6,15 @@ import 'package:hive/hive.dart';
|
|||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/encrypt.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/ios_legacy_helper.dart'
|
||||
as ios_legacy_helper;
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/secret_store_key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'package:cake_wallet/di.dart';
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
Future<void> loadCurrentWallet() async {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
import 'package:cake_wallet/entities/transaction_priority.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/entities/enumerable_item.dart';
|
||||
|
||||
class MoneroTransactionPriority extends TransactionPriority {
|
||||
const MoneroTransactionPriority({String title, int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const all = [
|
||||
MoneroTransactionPriority.slow,
|
||||
MoneroTransactionPriority.regular,
|
||||
MoneroTransactionPriority.medium,
|
||||
MoneroTransactionPriority.fast,
|
||||
MoneroTransactionPriority.fastest
|
||||
];
|
||||
static const slow = MoneroTransactionPriority(title: 'Slow', raw: 0);
|
||||
static const regular = MoneroTransactionPriority(title: 'Regular', raw: 1);
|
||||
static const medium = MoneroTransactionPriority(title: 'Medium', raw: 2);
|
||||
static const fast = MoneroTransactionPriority(title: 'Fast', raw: 3);
|
||||
static const fastest = MoneroTransactionPriority(title: 'Fastest', raw: 4);
|
||||
static const standard = slow;
|
||||
|
||||
|
||||
static List<MoneroTransactionPriority> forWalletType(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return MoneroTransactionPriority.all;
|
||||
case WalletType.bitcoin:
|
||||
return [
|
||||
MoneroTransactionPriority.slow,
|
||||
MoneroTransactionPriority.regular,
|
||||
MoneroTransactionPriority.fast
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static MoneroTransactionPriority deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return slow;
|
||||
case 1:
|
||||
return regular;
|
||||
case 2:
|
||||
return medium;
|
||||
case 3:
|
||||
return fast;
|
||||
case 4:
|
||||
return fastest;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (this) {
|
||||
case MoneroTransactionPriority.slow:
|
||||
return S.current.transaction_priority_slow;
|
||||
case MoneroTransactionPriority.regular:
|
||||
return S.current.transaction_priority_regular;
|
||||
case MoneroTransactionPriority.medium:
|
||||
return S.current.transaction_priority_medium;
|
||||
case MoneroTransactionPriority.fast:
|
||||
return S.current.transaction_priority_fast;
|
||||
case MoneroTransactionPriority.fastest:
|
||||
return S.current.transaction_priority_fastest;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/entities/digest_request.dart';
|
||||
|
||||
part 'node.g.dart';
|
||||
|
||||
Uri createUriFromElectrumAddress(String address) =>
|
||||
Uri.tryParse('tcp://$address');
|
||||
|
||||
@HiveType(typeId: Node.typeId)
|
||||
class Node extends HiveObject with Keyable {
|
||||
Node(
|
||||
{@required String uri,
|
||||
@required WalletType type,
|
||||
this.login,
|
||||
this.password,
|
||||
this.useSSL}) {
|
||||
uriRaw = uri;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
Node.fromMap(Map map)
|
||||
: uriRaw = map['uri'] as String ?? '',
|
||||
login = map['login'] as String,
|
||||
password = map['password'] as String,
|
||||
typeRaw = map['typeRaw'] as int,
|
||||
useSSL = map['useSSL'] as bool;
|
||||
|
||||
static const typeId = 1;
|
||||
static const boxName = 'Nodes';
|
||||
|
||||
@HiveField(0)
|
||||
String uriRaw;
|
||||
|
||||
@HiveField(1)
|
||||
String login;
|
||||
|
||||
@HiveField(2)
|
||||
String password;
|
||||
|
||||
@HiveField(3)
|
||||
int typeRaw;
|
||||
|
||||
@HiveField(4)
|
||||
bool useSSL;
|
||||
|
||||
bool get isSSL => useSSL ?? false;
|
||||
|
||||
Uri get uri {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return Uri.http(uriRaw, '');
|
||||
case WalletType.bitcoin:
|
||||
return createUriFromElectrumAddress(uriRaw);
|
||||
case WalletType.litecoin:
|
||||
return createUriFromElectrumAddress(uriRaw);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic get keyIndex {
|
||||
_keyIndex ??= key;
|
||||
return _keyIndex;
|
||||
}
|
||||
|
||||
WalletType get type => deserializeFromInt(typeRaw);
|
||||
|
||||
set type(WalletType type) => typeRaw = serializeToInt(type);
|
||||
|
||||
dynamic _keyIndex;
|
||||
|
||||
Future<bool> requestNode() async {
|
||||
try {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return requestMoneroNode();
|
||||
case WalletType.bitcoin:
|
||||
return requestElectrumServer();
|
||||
case WalletType.litecoin:
|
||||
return requestElectrumServer();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> requestMoneroNode() async {
|
||||
try {
|
||||
Map<String, dynamic> resBody;
|
||||
|
||||
if (login != null && password != null) {
|
||||
final digestRequest = DigestRequest();
|
||||
final response = await digestRequest.request(
|
||||
uri: uri.toString(), login: login, password: password);
|
||||
resBody = response.data as Map<String, dynamic>;
|
||||
} else {
|
||||
final rpcUri = Uri.http(uri.authority, '/json_rpc');
|
||||
final headers = {'Content-type': 'application/json'};
|
||||
final body =
|
||||
json.encode({'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'});
|
||||
final response =
|
||||
await http.post(rpcUri.toString(), headers: headers, body: body);
|
||||
resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
return !(resBody['result']['offline'] as bool);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> requestElectrumServer() async {
|
||||
try {
|
||||
await SecureSocket.connect(uri.host, uri.port,
|
||||
timeout: Duration(seconds: 5), onBadCertificate: (_) => true);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import "package:yaml/yaml.dart";
|
||||
import 'package:cake_wallet/entities/node.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
Future<List<Node>> loadDefaultNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue