config changes

This commit is contained in:
fosse 2023-07-24 16:23:09 -04:00
parent 359c5e8a9a
commit e3624880c5
35 changed files with 2108 additions and 344 deletions

View file

@ -0,0 +1,83 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_nano/api/signatures.dart';
import 'package:cw_nano/api/types.dart';
import 'package:cw_nano/api/monero_api.dart';
import 'package:cw_nano/api/structs/account_row.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_nano/api/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({required String label}) {
final labelPointer = label.toNativeUtf8();
accountAddNewNative(labelPointer);
calloc.free(labelPointer);
}
void setLabelForAccountSync({required int accountIndex, required String label}) {
final labelPointer = label.toNativeUtf8();
accountSetLabelNative(accountIndex, labelPointer);
calloc.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({required String label}) async {
await compute(_addAccount, label);
await store();
}
Future<void> setLabelForAccount({required int accountIndex, required String label}) async {
await compute(
_setLabelForAccount, {'accountIndex': accountIndex, 'label': label});
await store();
}

View file

@ -0,0 +1,8 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
String convertUTF8ToString({required Pointer<Utf8> pointer}) {
final str = pointer.toDartString();
calloc.free(pointer);
return str;
}

View file

@ -0,0 +1,5 @@
class ConnectionToNodeException implements Exception {
ConnectionToNodeException({required this.message});
final String message;
}

View file

@ -0,0 +1,8 @@
class CreationTransactionException implements Exception {
CreationTransactionException({required this.message});
final String message;
@override
String toString() => message;
}

View file

@ -0,0 +1,5 @@
class SetupWalletException implements Exception {
SetupWalletException({required this.message});
final String message;
}

View file

@ -0,0 +1,8 @@
class WalletCreationException implements Exception {
WalletCreationException({required this.message});
final String message;
@override
String toString() => message;
}

View file

@ -0,0 +1,8 @@
class WalletOpeningException implements Exception {
WalletOpeningException({required this.message});
final String message;
@override
String toString() => message;
}

View file

@ -0,0 +1,5 @@
class WalletRestoreFromKeysException implements Exception {
WalletRestoreFromKeysException({required this.message});
final String message;
}

View file

@ -0,0 +1,5 @@
class WalletRestoreFromSeedException implements Exception {
WalletRestoreFromSeedException({required this.message});
final String message;
}

View file

@ -0,0 +1,6 @@
import 'dart:ffi';
import 'dart:io';
final DynamicLibrary moneroApi = Platform.isAndroid
? DynamicLibrary.open("libcw_monero.so")
: DynamicLibrary.open("cw_monero.framework/cw_monero");

View file

@ -0,0 +1,6 @@
class MoneroOutput {
MoneroOutput({required this.address, required this.amount});
final String address;
final String amount;
}

View file

@ -0,0 +1,132 @@
import 'dart:ffi';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/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_password = Int8 Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
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();
typedef get_subaddress_label = Pointer<Utf8> Function(
Int32 accountIndex,
Int32 addressIndex);
typedef set_trusted_daemon = Void Function(Int8 trusted);
typedef trusted_daemon = Int8 Function();

View file

@ -0,0 +1,12 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class AccountRow extends Struct {
@Int64()
external int id;
external Pointer<Utf8> label;
String getLabel() => label.toDartString();
int getId() => id;
}

View file

@ -0,0 +1,39 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class PendingTransactionRaw extends Struct {
@Int64()
external int amount;
@Int64()
external int fee;
external Pointer<Utf8> hash;
external Pointer<Utf8> hex;
external Pointer<Utf8> txKey;
String getHash() => hash.toDartString();
String getHex() => hex.toDartString();
String getKey() => txKey.toDartString();
}
class PendingTransactionDescription {
PendingTransactionDescription({
required this.amount,
required this.fee,
required this.hash,
required this.hex,
required this.txKey,
required this.pointerAddress});
final int amount;
final int fee;
final String hash;
final String hex;
final String txKey;
final int pointerAddress;
}

View file

@ -0,0 +1,15 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class SubaddressRow extends Struct {
@Int64()
external int id;
external Pointer<Utf8> address;
external Pointer<Utf8> label;
String getLabel() => label.toDartString();
String getAddress() => address.toDartString();
int getId() => id;
}

View file

@ -0,0 +1,41 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class TransactionInfoRow extends Struct {
@Uint64()
external int amount;
@Uint64()
external int fee;
@Uint64()
external int blockHeight;
@Uint64()
external int confirmations;
@Uint32()
external int subaddrAccount;
@Int8()
external int direction;
@Int8()
external int isPending;
@Uint32()
external int subaddrIndex;
external Pointer<Utf8> hash;
external Pointer<Utf8> paymentId;
@Int64()
external int datetime;
int getDatetime() => datetime;
int getAmount() => amount >= 0 ? amount : amount * -1;
bool getIsPending() => isPending != 0;
String getHash() => hash.toDartString();
String getPaymentId() => paymentId.toDartString();
}

View file

@ -0,0 +1,8 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class Utf8Box extends Struct {
external Pointer<Utf8> value;
String getValue() => value.toDartString();
}

View file

@ -0,0 +1,97 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/structs/subaddress_row.dart';
import 'package:cw_monero/api/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({required int accountIndex, required String label}) {
final labelPointer = label.toNativeUtf8();
subaddrressAddNewNative(accountIndex, labelPointer);
calloc.free(labelPointer);
}
void setLabelForSubaddressSync(
{required int accountIndex, required int addressIndex, required String label}) {
final labelPointer = label.toNativeUtf8();
subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer);
calloc.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<void> addSubaddress({required int accountIndex, required String label}) async {
await compute<Map<String, Object>, void>(
_addSubaddress, {'accountIndex': accountIndex, 'label': label});
await store();
}
Future<void> setLabelForSubaddress(
{required int accountIndex, required int addressIndex, required String label}) async {
await compute<Map<String, Object>, void>(_setLabelForSubaddress, {
'accountIndex': accountIndex,
'addressIndex': addressIndex,
'label': label
});
await store();
}

View file

@ -0,0 +1,234 @@
import 'dart:ffi';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/monero_output.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/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 = txId.toNativeUtf8();
final keyPointer = getTxKeyNative(txIdPointer);
calloc.free(txIdPointer);
if (keyPointer != null) {
return convertUTF8ToString(pointer: keyPointer);
}
return '';
}
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(
{required String address,
required String paymentId,
required int priorityRaw,
String? amount,
int accountIndex = 0}) {
final addressPointer = address.toNativeUtf8();
final paymentIdPointer = paymentId.toNativeUtf8();
final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr;
final errorMessagePointer = calloc<Utf8Box>();
final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
final created = transactionCreateNative(
addressPointer,
paymentIdPointer,
amountPointer,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
0;
calloc.free(addressPointer);
calloc.free(paymentIdPointer);
if (amountPointer != nullptr) {
calloc.free(amountPointer);
}
if (!created) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
return PendingTransactionDescription(
amount: pendingTransactionRawPointer.ref.amount,
fee: pendingTransactionRawPointer.ref.fee,
hash: pendingTransactionRawPointer.ref.getHash(),
hex: pendingTransactionRawPointer.ref.getHex(),
txKey: pendingTransactionRawPointer.ref.getKey(),
pointerAddress: pendingTransactionRawPointer.address);
}
PendingTransactionDescription createTransactionMultDestSync(
{required List<MoneroOutput> outputs,
required String paymentId,
required int priorityRaw,
int accountIndex = 0}) {
final int size = outputs.length;
final List<Pointer<Utf8>> addressesPointers = outputs.map((output) =>
output.address.toNativeUtf8()).toList();
final Pointer<Pointer<Utf8>> addressesPointerPointer = calloc(size);
final List<Pointer<Utf8>> amountsPointers = outputs.map((output) =>
output.amount.toNativeUtf8()).toList();
final Pointer<Pointer<Utf8>> amountsPointerPointer = calloc(size);
for (int i = 0; i < size; i++) {
addressesPointerPointer[i] = addressesPointers[i];
amountsPointerPointer[i] = amountsPointers[i];
}
final paymentIdPointer = paymentId.toNativeUtf8();
final errorMessagePointer = calloc<Utf8Box>();
final pendingTransactionRawPointer = calloc<PendingTransactionRaw>();
final created = transactionCreateMultDestNative(
addressesPointerPointer,
paymentIdPointer,
amountsPointerPointer,
size,
priorityRaw,
accountIndex,
errorMessagePointer,
pendingTransactionRawPointer) !=
0;
calloc.free(addressesPointerPointer);
calloc.free(amountsPointerPointer);
addressesPointers.forEach((element) => calloc.free(element));
amountsPointers.forEach((element) => calloc.free(element));
calloc.free(paymentIdPointer);
if (!created) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
throw CreationTransactionException(message: message);
}
return PendingTransactionDescription(
amount: pendingTransactionRawPointer.ref.amount,
fee: pendingTransactionRawPointer.ref.fee,
hash: pendingTransactionRawPointer.ref.getHash(),
hex: pendingTransactionRawPointer.ref.getHex(),
txKey: pendingTransactionRawPointer.ref.getKey(),
pointerAddress: pendingTransactionRawPointer.address);
}
void commitTransactionFromPointerAddress({required int address}) => commitTransaction(
transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
void commitTransaction({required Pointer<PendingTransactionRaw> transactionPointer}) {
final errorMessagePointer = calloc<Utf8Box>();
final isCommited =
transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
if (!isCommited) {
final message = errorMessagePointer.ref.getValue();
calloc.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(
{required String address,
required int priorityRaw,
String? amount,
String paymentId = '',
int accountIndex = 0}) =>
compute(_createTransactionSync, {
'address': address,
'paymentId': paymentId,
'amount': amount,
'priorityRaw': priorityRaw,
'accountIndex': accountIndex
});
Future<PendingTransactionDescription> createTransactionMultDest(
{required List<MoneroOutput> outputs,
required int priorityRaw,
String paymentId = '',
int accountIndex = 0}) =>
compute(_createTransactionMultDestSync, {
'outputs': outputs,
'paymentId': paymentId,
'priorityRaw': priorityRaw,
'accountIndex': accountIndex
});

130
cw_nano/lib/api/types.dart Normal file
View file

@ -0,0 +1,130 @@
import 'dart:ffi';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/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 SetPassword = int Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
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();
typedef GetSubaddressLabel = Pointer<Utf8> Function(
int accountIndex,
int addressIndex);
typedef SetTrustedDaemon = void Function(int);
typedef TrustedDaemon = int Function();

374
cw_nano/lib/api/wallet.dart Normal file
View file

@ -0,0 +1,374 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/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 setPasswordNative =
moneroApi.lookup<NativeFunction<set_password>>('set_password').asFunction<SetPassword>();
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>();
final getSubaddressLabelNative = moneroApi
.lookup<NativeFunction<get_subaddress_label>>('get_subaddress_label')
.asFunction<GetSubaddressLabel>();
final setTrustedDaemonNative = moneroApi
.lookup<NativeFunction<set_trusted_daemon>>('set_trusted_daemon')
.asFunction<SetTrustedDaemon>();
final trustedDaemonNative = moneroApi
.lookup<NativeFunction<trusted_daemon>>('trusted_daemon')
.asFunction<TrustedDaemon>();
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(
{required String address,
String? login,
String? password,
bool useSSL = false,
bool isLightWallet = false}) {
final addressPointer = address.toNativeUtf8();
Pointer<Utf8>? loginPointer;
Pointer<Utf8>? passwordPointer;
if (login != null) {
loginPointer = login.toNativeUtf8();
}
if (password != null) {
passwordPointer = password.toNativeUtf8();
}
final errorMessagePointer = ''.toNativeUtf8();
final isSetupNode = setupNodeNative(
addressPointer,
loginPointer,
passwordPointer,
_boolToInt(useSSL),
_boolToInt(isLightWallet),
errorMessagePointer) !=
0;
calloc.free(addressPointer);
if (loginPointer != null) {
calloc.free(loginPointer);
}
if (passwordPointer != null) {
calloc.free(passwordPointer);
}
if (!isSetupNode) {
throw SetupWalletException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
return isSetupNode;
}
void startRefreshSync() => startRefreshNative();
Future<bool> connectToNode() async => connecToNodeNative() != 0;
void setRefreshFromBlockHeight({required int height}) =>
setRefreshFromBlockHeightNative(height);
void setRecoveringFromSeed({required bool isRecovery}) =>
setRecoveringFromSeedNative(_boolToInt(isRecovery));
void storeSync() {
final pathPointer = ''.toNativeUtf8();
storeNative(pathPointer);
calloc.free(pathPointer);
}
void setPasswordSync(String password) {
final passwordPointer = password.toNativeUtf8();
final errorMessagePointer = calloc<Utf8Box>();
final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0;
calloc.free(passwordPointer);
if (!changed) {
final message = errorMessagePointer.ref.getValue();
calloc.free(errorMessagePointer);
throw Exception(message);
}
calloc.free(errorMessagePointer);
}
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();
}
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<String, Object?> 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<void> setupNode(
{required 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<void> store() => compute<int, void>(_storeSync, 0);
Future<bool> isConnected() => compute(_isConnected, 0);
Future<int> getNodeHeight() => compute(_getNodeHeight, 0);
void rescanBlockchainAsync() => rescanBlockchainAsyncNative();
String getSubaddressLabel(int accountIndex, int addressIndex) {
return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex));
}
Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted));
Future<bool> trustedDaemon() async => trustedDaemonNative() != 0;

View file

@ -0,0 +1,254 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
import 'package:cw_monero/api/monero_api.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart';
import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
import 'package:cw_monero/api/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(
{required String path,
required String password,
required String language,
int nettype = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final languagePointer = language.toNativeUtf8();
final errorMessagePointer = ''.toNativeUtf8();
final isWalletCreated = createWalletNative(pathPointer, passwordPointer,
languagePointer, nettype, errorMessagePointer) !=
0;
calloc.free(pathPointer);
calloc.free(passwordPointer);
calloc.free(languagePointer);
if (!isWalletCreated) {
throw WalletCreationException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
// setupNodeSync(address: "node.moneroworld.com:18089");
}
bool isWalletExistSync({required String path}) {
final pathPointer = path.toNativeUtf8();
final isExist = isWalletExistNative(pathPointer) != 0;
calloc.free(pathPointer);
return isExist;
}
void restoreWalletFromSeedSync(
{required String path,
required String password,
required String seed,
int nettype = 0,
int restoreHeight = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final seedPointer = seed.toNativeUtf8();
final errorMessagePointer = ''.toNativeUtf8();
final isWalletRestored = restoreWalletFromSeedNative(
pathPointer,
passwordPointer,
seedPointer,
nettype,
restoreHeight,
errorMessagePointer) !=
0;
calloc.free(pathPointer);
calloc.free(passwordPointer);
calloc.free(seedPointer);
if (!isWalletRestored) {
throw WalletRestoreFromSeedException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
}
void restoreWalletFromKeysSync(
{required String path,
required String password,
required String language,
required String address,
required String viewKey,
required String spendKey,
int nettype = 0,
int restoreHeight = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final languagePointer = language.toNativeUtf8();
final addressPointer = address.toNativeUtf8();
final viewKeyPointer = viewKey.toNativeUtf8();
final spendKeyPointer = spendKey.toNativeUtf8();
final errorMessagePointer = ''.toNativeUtf8();
final isWalletRestored = restoreWalletFromKeysNative(
pathPointer,
passwordPointer,
languagePointer,
addressPointer,
viewKeyPointer,
spendKeyPointer,
nettype,
restoreHeight,
errorMessagePointer) !=
0;
calloc.free(pathPointer);
calloc.free(passwordPointer);
calloc.free(languagePointer);
calloc.free(addressPointer);
calloc.free(viewKeyPointer);
calloc.free(spendKeyPointer);
if (!isWalletRestored) {
throw WalletRestoreFromKeysException(
message: convertUTF8ToString(pointer: errorMessagePointer));
}
}
void loadWallet({
required String path,
required String password,
int nettype = 0}) {
final pathPointer = path.toNativeUtf8();
final passwordPointer = password.toNativeUtf8();
final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0;
calloc.free(pathPointer);
calloc.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'] as String, password: args['password'] as String);
bool _isWalletExist(String path) => isWalletExistSync(path: path);
void openWallet({required String path, required 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(
{required String path,
required String password,
required String language,
int nettype = 0}) async =>
compute(_createWallet, {
'path': path,
'password': password,
'language': language,
'nettype': nettype
});
Future<void> restoreFromSeed(
{required String path,
required String password,
required 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<void> restoreFromKeys(
{required String path,
required String password,
required String language,
required String address,
required String viewKey,
required 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({required String path}) => compute(_isWalletExist, path);

View file

@ -0,0 +1,82 @@
import 'package:cw_core/monero_amount_format.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/account.dart';
import 'package:cw_nano/api/account_list.dart' as account_list;
import 'package:cw_nano/api/wallet.dart' as monero_wallet;
part 'nano_account_list.g.dart';
class NanoAccountList = NanoAccountListBase with _$NanoAccountList;
abstract class NanoAccountListBase with Store {
NanoAccountListBase()
: accounts = ObservableList<Account>(),
_isRefreshing = false,
_isUpdating = false {
refresh();
}
@observable
ObservableList<Account> accounts;
bool _isRefreshing;
bool _isUpdating;
void update() async {
if (_isUpdating) {
return;
}
try {
_isUpdating = true;
refresh();
final accounts = getAll();
if (accounts.isNotEmpty) {
this.accounts.clear();
this.accounts.addAll(accounts);
}
_isUpdating = false;
} catch (e) {
_isUpdating = false;
rethrow;
}
}
List<Account> getAll() => account_list.getAllAccount().map((accountRow) {
final accountIndex = accountRow.getId();
final balance = monero_wallet.getFullBalance(accountIndex: accountIndex);
return Account(
id: accountRow.getId(),
label: accountRow.getLabel(),
balance: moneroAmountToString(amount: balance),
);
}).toList();
Future<void> addAccount({required String label}) async {
await account_list.addAccount(label: label);
update();
}
Future<void> setLabelAccount({required int accountIndex, required String label}) async {
await account_list.setLabelForAccount(accountIndex: accountIndex, label: label);
update();
}
void refresh() {
if (_isRefreshing) {
return;
}
try {
_isRefreshing = true;
account_list.refreshAccounts();
_isRefreshing = false;
} catch (e) {
_isRefreshing = false;
print(e);
rethrow;
}
}
}

View file

@ -24,7 +24,6 @@ class NanoBalance extends Balance {
// super(moneroParseAmount(amount: formattedReceivableBalance),
// moneroParseAmount(amount: formattedCurrentBalance));
@override
String get formattedAvailableBalance => "error";

View file

@ -0,0 +1,28 @@
import 'dart:core';
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_nano/nano_transaction_info.dart';
part 'nano_transaction_history.g.dart';
class NanoTransactionHistory = NanoTransactionHistoryBase
with _$NanoTransactionHistory;
abstract class NanoTransactionHistoryBase
extends TransactionHistoryBase<NanoTransactionInfo> with Store {
NanoTransactionHistoryBase() {
transactions = ObservableMap<String, NanoTransactionInfo>();
}
@override
Future<void> save() async {}
@override
void addOne(NanoTransactionInfo transaction) =>
transactions[transaction.id] = transaction;
@override
void addMany(Map<String, MoneroTransactionInfo> transactions) =>
this.transactions.addAll(transactions);
}

View file

@ -3,23 +3,11 @@ import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_monero/monero_transaction_creation_exception.dart';
import 'package:cw_monero/monero_transaction_info.dart';
import 'package:cw_monero/monero_wallet_addresses.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_nano/nano_balance.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_monero/api/transaction_history.dart'
as monero_transaction_history;
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
import 'package:cw_monero/api/transaction_history.dart' as transaction_history;
import 'package:cw_monero/api/monero_output.dart';
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
import 'package:cw_monero/pending_monero_transaction.dart';
import 'package:cw_core/monero_wallet_keys.dart';
import 'package:cw_core/monero_balance.dart';
import 'package:cw_monero/monero_transaction_history.dart';
import 'package:cw_core/account.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/wallet_base.dart';
@ -35,33 +23,30 @@ const moneroBlockSize = 1000;
class NanoWallet = NanoWalletBase with _$NanoWallet;
abstract class NanoWalletBase extends WalletBase<MoneroBalance,
MoneroTransactionHistory, MoneroTransactionInfo> with Store {
abstract class NanoWalletBase
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
NanoWalletBase({required WalletInfo walletInfo})
: balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
CryptoCurrency.xmr: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0))
}),
: balance = ObservableMap<CryptoCurrency, NanoBalance>.of({
CryptoCurrency.nano: NanoBalance(
currentBalance: nano_wallet.getFullBalance(accountIndex: 0),
receivableBalance: nano_wallet.getFullBalance(accountIndex: 0))
}),
_isTransactionUpdating = false,
_hasSyncAfterStartup = false,
walletAddresses = NanoWalletAddresses(walletInfo),
syncStatus = NotConnectedSyncStatus(),
super(walletInfo) {
transactionHistory = MoneroTransactionHistory();
_onAccountChangeReaction = reaction((_) => walletAddresses.account,
(Account? account) {
_onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) {
if (account == null) {
return;
}
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(
<CryptoCurrency, MoneroBalance>{
currency: MoneroBalance(
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{
currency: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: account.id))
});
unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id))
});
walletAddresses.updateSubaddressList(accountIndex: account.id);
});
}
@ -97,12 +82,12 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
Future<void> init() async {
await walletAddresses.init();
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(
<CryptoCurrency, MoneroBalance>{
currency: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id),
unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id))
});
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{
currency: MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id),
unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id))
});
_setListeners();
await updateTransactions();
@ -110,8 +95,7 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
monero_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery);
if (monero_wallet.getCurrentHeight() <= 1) {
monero_wallet.setRefreshFromBlockHeight(
height: walletInfo.restoreHeight);
monero_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight);
}
}
@ -119,6 +103,7 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
// Duration(seconds: _autoSaveInterval),
// (_) async => await save());
}
@override
Future<void>? updateBalance() => null;
@ -134,9 +119,7 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
try {
syncStatus = ConnectingSyncStatus();
await monero_wallet.setupNode(
address: node.uri.toString(),
useSSL: node.isSSL,
isLightWallet: false);
address: node.uri.toString(), useSSL: node.isSSL, isLightWallet: false);
monero_wallet.setTrustedDaemon(node.trusted);
syncStatus = ConnectedSyncStatus();
@ -166,96 +149,14 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
@override
Future<PendingTransaction> createTransaction(Object credentials) async {
final _credentials = credentials as MoneroTransactionCreationCredentials;
final outputs = _credentials.outputs;
final hasMultiDestination = outputs.length > 1;
final unlockedBalance =
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
PendingTransactionDescription pendingTransactionDescription;
if (!(syncStatus is SyncedSyncStatus)) {
throw MoneroTransactionCreationException('The wallet is not synced.');
}
if (hasMultiDestination) {
if (outputs.any((item) => item.sendAll
|| (item.formattedCryptoAmount ?? 0) <= 0)) {
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
}
final int totalAmount = outputs.fold(0, (acc, value) =>
acc + (value.formattedCryptoAmount ?? 0));
if (unlockedBalance < totalAmount) {
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
}
final moneroOutputs = outputs.map((output) {
final outputAddress = output.isParsedAddress
? output.extractedAddress
: output.address;
return MoneroOutput(
address: outputAddress!,
amount: output.cryptoAmount!.replaceAll(',', '.'));
}).toList();
pendingTransactionDescription =
await transaction_history.createTransactionMultDest(
outputs: moneroOutputs,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account!.id);
} else {
final output = outputs.first;
final address = output.isParsedAddress
? output.extractedAddress
: output.address;
final amount = output.sendAll
? null
: output.cryptoAmount!.replaceAll(',', '.');
final formattedAmount = output.sendAll
? null
: output.formattedCryptoAmount;
if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
(formattedAmount == null && unlockedBalance <= 0)) {
final formattedBalance = moneroAmountToString(amount: unlockedBalance);
throw MoneroTransactionCreationException(
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
}
pendingTransactionDescription =
await transaction_history.createTransaction(
address: address!,
amount: amount,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account!.id);
}
return PendingMoneroTransaction(pendingTransactionDescription);
// final _credentials = credentials as MoneroTransactionCreationCredentials;
// return null;
throw UnimplementedError();
}
@override
int calculateEstimatedFee(TransactionPriority priority, int? amount) {
// FIXME: hardcoded value;
if (priority is MoneroTransactionPriority) {
switch (priority) {
case MoneroTransactionPriority.slow:
return 24590000;
case MoneroTransactionPriority.automatic:
return 123050000;
case MoneroTransactionPriority.medium:
return 245029999;
case MoneroTransactionPriority.fast:
return 614530000;
case MoneroTransactionPriority.fastest:
return 26021600000;
}
}
return 0;
}
@ -272,10 +173,8 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
try {
// -- rename the waller folder --
final currentWalletDir =
Directory(await pathForWalletDir(name: name, type: type));
final newWalletDirPath =
await pathForWalletDir(name: newWalletName, type: type);
final currentWalletDir = Directory(await pathForWalletDir(name: name, type: type));
final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type);
await currentWalletDir.rename(newWalletDirPath);
// -- use new waller folder to rename files with old names still --
@ -285,8 +184,7 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
final currentKeysFile = File('$renamedWalletPath.keys');
final currentAddressListFile = File('$renamedWalletPath.address.txt');
final newWalletPath =
await pathForWallet(name: newWalletName, type: type);
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
if (currentCacheFile.existsSync()) {
await currentCacheFile.rename(newWalletPath);
@ -304,8 +202,7 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
final currentKeysFile = File('$currentWalletPath.keys');
final currentAddressListFile = File('$currentWalletPath.address.txt');
final newWalletPath =
await pathForWallet(name: newWalletName, type: type);
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
// Copies current wallet files into new wallet name's dir and files
if (currentCacheFile.existsSync()) {
@ -352,16 +249,14 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
}
String getTransactionAddress(int accountIndex, int addressIndex) =>
monero_wallet.getAddress(
accountIndex: accountIndex,
addressIndex: addressIndex);
monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex);
@override
Future<Map<String, MoneroTransactionInfo>> fetchTransactions() async {
monero_transaction_history.refreshTransactions();
return _getAllTransactions(null).fold<Map<String, MoneroTransactionInfo>>(
<String, MoneroTransactionInfo>{},
(Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
return _getAllTransactions(null)
.fold<Map<String, MoneroTransactionInfo>>(<String, MoneroTransactionInfo>{},
(Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
acc[tx.id] = tx;
return acc;
});
@ -388,11 +283,10 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
return monero_wallet.getSubaddressLabel(accountIndex, addressIndex);
}
List<MoneroTransactionInfo> _getAllTransactions(dynamic _) =>
monero_transaction_history
.getAllTransations()
.map((row) => MoneroTransactionInfo.fromRow(row))
.toList();
List<MoneroTransactionInfo> _getAllTransactions(dynamic _) => monero_transaction_history
.getAllTransations()
.map((row) => MoneroTransactionInfo.fromRow(row))
.toList();
void _setListeners() {
_listener?.stop();
@ -414,8 +308,7 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
}
int _getHeightDistance(DateTime date) {
final distance =
DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch;
final distance = DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch;
final daysTmp = (distance / 86400).round();
final days = daysTmp < 1 ? 1 : daysTmp;
@ -439,16 +332,13 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
if (balance[currency]!.fullBalance != fullBalance ||
balance[currency]!.unlockedBalance != unlockedBalance) {
balance[currency] = MoneroBalance(
fullBalance: fullBalance, unlockedBalance: unlockedBalance);
balance[currency] = MoneroBalance(fullBalance: fullBalance, unlockedBalance: unlockedBalance);
}
}
Future<void> _askForUpdateTransactionHistory() async =>
await updateTransactions();
Future<void> _askForUpdateTransactionHistory() async => await updateTransactions();
int _getFullBalance() =>
monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id);
int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id);
int _getUnlockedBalance() =>
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
@ -468,9 +358,9 @@ abstract class NanoWalletBase extends WalletBase<MoneroBalance,
syncStatus = SyncedSyncStatus();
if (!_hasSyncAfterStartup) {
_hasSyncAfterStartup = true;
await save();
}
_hasSyncAfterStartup = true;
await save();
}
if (walletInfo.isRecovery) {
await setAsRecovered();

View file

@ -0,0 +1,45 @@
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/account.dart';
import 'package:cw_nano/nano_account_list.dart';
import 'package:cw_core/subaddress.dart';
import 'package:mobx/mobx.dart';
part 'monero_wallet_addresses.g.dart';
class NanoWalletAddresses = NanoWalletAddressesBase
with _$NanoWalletAddresses;
abstract class NanoWalletAddressesBase extends WalletAddresses with Store {
NanoWalletAddressesBase(WalletInfo walletInfo)
: accountList = NanoAccountList(),
address = '',
super(walletInfo);
@override
@observable
String address;
@observable
Account? account;
NanoAccountList accountList;
@override
Future<void> init() async {
accountList.update();
account = accountList.accounts.first;
}
bool validate() {
accountList.update();
final accountListLength = accountList.accounts.length ?? 0;
if (accountListLength <= 0) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,26 @@
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
class NanoNewWalletCredentials extends WalletCredentials {
NanoNewWalletCredentials({required String name, WalletInfo? walletInfo})
: super(name: name, walletInfo: walletInfo);
}
class NanoRestoreWalletFromSeedCredentials extends WalletCredentials {
NanoRestoreWalletFromSeedCredentials(
{required String name,
required String password,
required this.mnemonic,
WalletInfo? walletInfo})
: super(name: name, password: password, walletInfo: walletInfo);
final String mnemonic;
}
class NanoRestoreWalletFromWIFCredentials extends WalletCredentials {
NanoRestoreWalletFromWIFCredentials(
{required String name, required String password, required this.wif, WalletInfo? walletInfo})
: super(name: name, password: password, walletInfo: walletInfo);
final String wif;
}

View file

@ -0,0 +1,103 @@
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
// import 'package:cw_nano/nano_mnemonics.dart';
import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
import 'package:hive/hive.dart';
import 'package:bip39/bip39.dart' as bip39;
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromWIFCredentials> {
NanoWalletService(this.walletInfoSource);
final Box<WalletInfo> walletInfoSource;
@override
Future<NanoWallet> create(NanoNewWalletCredentials credentials) async {
final mnemonic = bip39.generateMnemonic();
final wallet = NanoWallet(
walletInfo: credentials.walletInfo!,
mnemonic: mnemonic,
password: credentials.password!,
);
await wallet.init();
await wallet.save();
return wallet;
}
@override
WalletType getType() => WalletType.ethereum;
@override
Future<bool> isWalletExit(String name) async =>
File(await pathForWallet(name: name, type: getType())).existsSync();
@override
Future<NanoWallet> openWallet(String name, String password) async {
final walletInfo =
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final wallet = await NanoWalletBase.open(
name: name,
password: password,
walletInfo: walletInfo,
);
await wallet.init();
await wallet.save();
return wallet;
}
@override
Future<void> remove(String wallet) async =>
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
@override
Future<NanoWallet> restoreFromKeys(credentials) {
throw UnimplementedError();
}
@override
Future<NanoWallet> restoreFromSeed(
NanoRestoreWalletFromSeedCredentials credentials) async {
// if (!bip39.validateMnemonic(credentials.mnemonic)) {
// throw EthereumMnemonicIsIncorrectException();
// }
final wallet = await NanoWallet(
// password: credentials.password!,
// mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!,
);
await wallet.init();
await wallet.save();
return wallet;
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = await NanoWalletBase.open(
password: password, name: currentName, walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
}

View file

@ -1,121 +1,105 @@
part of 'nano.dart';
// class CWMoneroAccountList extends MoneroAccountList {
// CWMoneroAccountList(this._wallet);
// final Object _wallet;
// @override
// @computed
// ObservableList<Account> get accounts {
// final moneroWallet = _wallet as MoneroWallet;
// final accounts = moneroWallet.walletAddresses.accountList
// .accounts
// .map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
// .toList();
// return ObservableList<Account>.of(accounts);
// }
// @override
// void update(Object wallet) {
// final moneroWallet = wallet as MoneroWallet;
// moneroWallet.walletAddresses.accountList.update();
// }
// @override
// void refresh(Object wallet) {
// final moneroWallet = wallet as MoneroWallet;
// moneroWallet.walletAddresses.accountList.refresh();
// }
// @override
// List<Account> getAll(Object wallet) {
// final moneroWallet = wallet as MoneroWallet;
// return moneroWallet.walletAddresses.accountList
// .getAll()
// .map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
// .toList();
// }
// @override
// Future<void> addAccount(Object wallet, {required String label}) async {
// final moneroWallet = wallet as MoneroWallet;
// await moneroWallet.walletAddresses.accountList.addAccount(label: label);
// }
// @override
// Future<void> setLabelAccount(Object wallet, {required int accountIndex, required String label}) async {
// final moneroWallet = wallet as MoneroWallet;
// await moneroWallet.walletAddresses.accountList
// .setLabelAccount(
// accountIndex: accountIndex,
// label: label);
// }
// }
class CWNano extends Nano {
@override
List<String> getNanoWordList(String language) => EthereumMnemonics.englishWordlist;
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) =>
EthereumWalletService(walletInfoSource);
// @override
// NanoAccountList getAccountList(Object wallet) {
// return CWNanoAccountList(wallet);
// }
@override
WalletCredentials createEthereumNewWalletCredentials({
List<String> getNanoWordList(String language) {
throw UnimplementedError();
}
@override
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource) {
return NanoWalletService(walletInfoSource);
}
// @override
// WalletCredentials createNanoNewWalletCredentials({
// required String name,
// WalletInfo? walletInfo,
// }) =>
// NanoNewWalletCredentials(name: name, walletInfo: walletInfo);
@override
WalletCredentials createNanoNewWalletCredentials({
required String name,
WalletInfo? walletInfo,
}) =>
EthereumNewWalletCredentials(name: name, walletInfo: walletInfo);
@override
WalletCredentials createEthereumRestoreWalletFromSeedCredentials({
required String name,
required String mnemonic,
required String password,
}) =>
EthereumRestoreWalletFromSeedCredentials(name: name, password: password, mnemonic: mnemonic);
@override
String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address;
@override
TransactionPriority getDefaultTransactionPriority() => EthereumTransactionPriority.medium;
@override
List<TransactionPriority> getTransactionPriorities() => EthereumTransactionPriority.all;
@override
TransactionPriority deserializeEthereumTransactionPriority(int raw) =>
EthereumTransactionPriority.deserialize(raw: raw);
@override
int getEstimatedFee(Object wallet, TransactionPriority priority) {
final ethereumWallet = wallet as EthereumWallet;
return ethereumWallet.feeRate(priority);
}
Object createEthereumTransactionCredentials(
List<Output> outputs, {
required TransactionPriority priority,
required CryptoCurrency currency,
int? feeRate,
}) =>
EthereumTransactionCredentials(
outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as EthereumTransactionPriority,
currency: currency,
feeRate: feeRate,
);
Object createEthereumTransactionCredentialsRaw(
List<OutputInfo> outputs, {
TransactionPriority? priority,
required CryptoCurrency currency,
required int feeRate,
}) =>
EthereumTransactionCredentials(
outputs,
priority: priority as EthereumTransactionPriority?,
currency: currency,
feeRate: feeRate,
);
@override
int formatterEthereumParseAmount(String amount) => EthereumFormatter.parseEthereumAmount(amount);
@override
double formatterEthereumAmountToDouble({required TransactionInfo transaction}) {
transaction as EthereumTransactionInfo;
return cryptoAmountToDouble(
amount: transaction.amount, divider: BigInt.from(10).pow(transaction.exponent).toInt());
required String language,
String? password,
}) {
return NanoNewWalletCredentials(name: name, password: password, language: language);
}
@override
List<Erc20Token> getERC20Currencies(WalletBase wallet) {
final ethereumWallet = wallet as EthereumWallet;
return ethereumWallet.erc20Currencies;
TransactionHistoryBase getTransactionHistory(Object wallet) {
// final moneroWallet = wallet as MoneroWallet;
// return moneroWallet.transactionHistory;
throw UnimplementedError();
}
@override
Future<void> addErc20Token(WalletBase wallet, Erc20Token token) async =>
await (wallet as EthereumWallet).addErc20Token(token);
@override
Future<void> deleteErc20Token(WalletBase wallet, Erc20Token token) async =>
await (wallet as EthereumWallet).deleteErc20Token(token);
@override
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress) async {
final ethereumWallet = wallet as EthereumWallet;
return await ethereumWallet.getErc20Token(contractAddress);
void onStartup() {
// monero_wallet_api.onStartup();
}
@override
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
transaction as EthereumTransactionInfo;
if (transaction.tokenSymbol == CryptoCurrency.eth.title) {
return CryptoCurrency.eth;
}
wallet as EthereumWallet;
return wallet.erc20Currencies
.firstWhere((element) => transaction.tokenSymbol == element.symbol);
List<String> getMoneroWordList(String language) {
throw UnimplementedError();
}
}

View file

@ -1,61 +1,59 @@
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/crypto_amount_format.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/output_info.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_ethereum/ethereum_formatter.dart';
import 'package:cw_ethereum/ethereum_mnemonics.dart';
import 'package:cw_ethereum/ethereum_transaction_credentials.dart';
import 'package:cw_ethereum/ethereum_transaction_info.dart';
import 'package:cw_ethereum/ethereum_wallet.dart';
import 'package:cw_ethereum/ethereum_wallet_creation_credentials.dart';
import 'package:cw_ethereum/ethereum_wallet_service.dart';
import 'package:cw_ethereum/ethereum_transaction_priority.dart';
import 'package:hive/hive.dart';
import 'package:cw_nano/api/wallet.dart' as nano_wallet_api;
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
part 'cw_nano.dart';
Nano? nano = CWNano();
abstract class Nano {
List<String> getNanoWordList(String language);
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource);
WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo});
WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
String getAddress(WalletBase wallet);
TransactionPriority getDefaultTransactionPriority();
List<TransactionPriority> getTransactionPriorities();
TransactionPriority deserializeEthereumTransactionPriority(int raw);
int getEstimatedFee(Object wallet, TransactionPriority priority);
Object createEthereumTransactionCredentials(
List<Output> outputs, {
required TransactionPriority priority,
required CryptoCurrency currency,
int? feeRate,
});
Object createEthereumTransactionCredentialsRaw(
List<OutputInfo> outputs, {
TransactionPriority? priority,
required CryptoCurrency currency,
required int feeRate,
});
int formatterEthereumParseAmount(String amount);
double formatterEthereumAmountToDouble({required TransactionInfo transaction});
List<Erc20Token> getERC20Currencies(WalletBase wallet);
Future<void> addErc20Token(WalletBase wallet, Erc20Token token);
Future<void> deleteErc20Token(WalletBase wallet, Erc20Token token);
Future<Erc20Token?> getErc20Token(WalletBase wallet, String contractAddress);
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction);
class Account {
Account({required this.id, required this.label, this.balance});
final int id;
final String label;
final String? balance;
}
abstract class NanoWalletDetails {
@observable
late Account account;
@observable
late NanoBalance balance;
}
abstract class Nano {
// NanoAccountList getAccountList(Object wallet);
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource);
TransactionHistoryBase getTransactionHistory(Object wallet);
NanoWalletDetails getNanoWalletDetails(Object wallet);
WalletCredentials createNanoNewWalletCredentials({
required String name,
required String language,
String password,
});
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex);
void onStartup();
List<String> getNanoWordList(String language);
}
abstract class NanoAccountList {
ObservableList<Account> get accounts;
void update(Object wallet);
void refresh(Object wallet);
List<Account> getAll(Object wallet);
Future<void> addAccount(Object wallet, {required String label});
Future<void> setLabelAccount(Object wallet, {required int accountIndex, required String label});
}

View file

@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
@ -45,6 +46,8 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
name: name, language: options as String);
case WalletType.ethereum:
return ethereum!.createEthereumNewWalletCredentials(name: name);
case WalletType.nano:
return nano!.createNanoNewWalletCredentials(name: name, language: options as String);
default:
throw Exception('Unexpected type: ${type.toString()}');;
}

1
model_generator.sh Normal file → Executable file
View file

@ -3,4 +3,5 @@ cd cw_monero && flutter pub get && flutter packages pub run build_runner build -
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_ethereum && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
cd cw_nano && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
flutter packages pub run build_runner build --delete-conflicting-outputs

View file

@ -10,7 +10,7 @@ case $APP_ANDROID_TYPE in
CONFIG_ARGS="--monero"
;;
$CAKEWALLET)
CONFIG_ARGS="--monero --bitcoin --haven --ethereum"
CONFIG_ARGS="--monero --bitcoin --haven --ethereum --nano"
;;
$HAVEN)
CONFIG_ARGS="--haven"

View file

@ -14,12 +14,32 @@ Future<void> main(List<String> args) async {
final hasMonero = args.contains('${prefix}monero');
final hasHaven = args.contains('${prefix}haven');
final hasEthereum = args.contains('${prefix}ethereum');
final hasNano = args.contains('${prefix}nano');
final hasBanano = args.contains('${prefix}banano');
await generateBitcoin(hasBitcoin);
await generateMonero(hasMonero);
await generateHaven(hasHaven);
await generateEthereum(hasEthereum);
await generatePubspec(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum);
await generateWalletTypes(hasMonero: hasMonero, hasBitcoin: hasBitcoin, hasHaven: hasHaven, hasEthereum: hasEthereum);
await generateNano(hasNano);
// await generateBanano(hasEthereum);
await generatePubspec(
hasMonero: hasMonero,
hasBitcoin: hasBitcoin,
hasHaven: hasHaven,
hasEthereum: hasEthereum,
hasNano: hasNano,
hasBanano: hasBanano,
);
await generateWalletTypes(
hasMonero: hasMonero,
hasBitcoin: hasBitcoin,
hasHaven: hasHaven,
hasEthereum: hasEthereum,
hasNano: hasNano,
hasBanano: hasBanano,
);
}
Future<void> generateBitcoin(bool hasImplementation) async {
@ -105,12 +125,12 @@ abstract class Bitcoin {
const bitcoinEmptyDefinition = 'Bitcoin? bitcoin;\n';
const bitcoinCWDefinition = 'Bitcoin? bitcoin = CWBitcoin();\n';
final output = '$bitcoinCommonHeaders\n'
+ (hasImplementation ? '$bitcoinCWHeaders\n' : '\n')
+ (hasImplementation ? '$bitcoinCwPart\n\n' : '\n')
+ (hasImplementation ? bitcoinCWDefinition : bitcoinEmptyDefinition)
+ '\n'
+ bitcoinContent;
final output = '$bitcoinCommonHeaders\n' +
(hasImplementation ? '$bitcoinCWHeaders\n' : '\n') +
(hasImplementation ? '$bitcoinCwPart\n\n' : '\n') +
(hasImplementation ? bitcoinCWDefinition : bitcoinEmptyDefinition) +
'\n' +
bitcoinContent;
if (outputFile.existsSync()) {
await outputFile.delete();
@ -279,12 +299,12 @@ abstract class MoneroAccountList {
const moneroEmptyDefinition = 'Monero? monero;\n';
const moneroCWDefinition = 'Monero? monero = CWMonero();\n';
final output = '$moneroCommonHeaders\n'
+ (hasImplementation ? '$moneroCWHeaders\n' : '\n')
+ (hasImplementation ? '$moneroCwPart\n\n' : '\n')
+ (hasImplementation ? moneroCWDefinition : moneroEmptyDefinition)
+ '\n'
+ moneroContent;
final output = '$moneroCommonHeaders\n' +
(hasImplementation ? '$moneroCWHeaders\n' : '\n') +
(hasImplementation ? '$moneroCwPart\n\n' : '\n') +
(hasImplementation ? moneroCWDefinition : moneroEmptyDefinition) +
'\n' +
moneroContent;
if (outputFile.existsSync()) {
await outputFile.delete();
@ -294,7 +314,6 @@ abstract class MoneroAccountList {
}
Future<void> generateHaven(bool hasImplementation) async {
final outputFile = File(havenOutputPath);
const havenCommonHeaders = """
import 'package:mobx/mobx.dart';
@ -459,12 +478,12 @@ abstract class HavenAccountList {
const havenEmptyDefinition = 'Haven? haven;\n';
const havenCWDefinition = 'Haven? haven = CWHaven();\n';
final output = '$havenCommonHeaders\n'
+ (hasImplementation ? '$havenCWHeaders\n' : '\n')
+ (hasImplementation ? '$havenCwPart\n\n' : '\n')
+ (hasImplementation ? havenCWDefinition : havenEmptyDefinition)
+ '\n'
+ havenContent;
final output = '$havenCommonHeaders\n' +
(hasImplementation ? '$havenCWHeaders\n' : '\n') +
(hasImplementation ? '$havenCwPart\n\n' : '\n') +
(hasImplementation ? havenCWDefinition : havenEmptyDefinition) +
'\n' +
havenContent;
if (outputFile.existsSync()) {
await outputFile.delete();
@ -474,7 +493,6 @@ abstract class HavenAccountList {
}
Future<void> generateEthereum(bool hasImplementation) async {
final outputFile = File(ethereumOutputPath);
const ethereumCommonHeaders = """
""";
@ -541,12 +559,12 @@ abstract class Ethereum {
const ethereumEmptyDefinition = 'Ethereum? ethereum;\n';
const ethereumCWDefinition = 'Ethereum? ethereum = CWEthereum();\n';
final output = '$ethereumCommonHeaders\n'
+ (hasImplementation ? '$ethereumCWHeaders\n' : '\n')
+ (hasImplementation ? '$ethereumCwPart\n\n' : '\n')
+ (hasImplementation ? ethereumCWDefinition : ethereumEmptyDefinition)
+ '\n'
+ ethereumContent;
final output = '$ethereumCommonHeaders\n' +
(hasImplementation ? '$ethereumCWHeaders\n' : '\n') +
(hasImplementation ? '$ethereumCwPart\n\n' : '\n') +
(hasImplementation ? ethereumCWDefinition : ethereumEmptyDefinition) +
'\n' +
ethereumContent;
if (outputFile.existsSync()) {
await outputFile.delete();
@ -555,8 +573,81 @@ abstract class Ethereum {
await outputFile.writeAsString(output);
}
Future<void> generatePubspec({required bool hasMonero, required bool hasBitcoin, required bool hasHaven, required bool hasEthereum}) async {
const cwCore = """
Future<void> generateNano(bool hasImplementation) async {
final outputFile = File(ethereumOutputPath);
const nanoCommonHeaders = """
""";
const nanoCWHeaders = """
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:hive/hive.dart';
import 'package:cw_nano/api/wallet.dart' as nano_wallet_api;
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_wallet_creation_credentials.dart';
""";
const nanoCwPart = "part 'cw_nano.dart';";
const nanoContent = """
abstract class Nano {
// NanoAccountList getAccountList(Object wallet);
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource);
TransactionHistoryBase getTransactionHistory(Object wallet);
NanoWalletDetails getNanoWalletDetails(Object wallet);
WalletCredentials createNanoNewWalletCredentials({
required String name,
required String language,
String password,
});
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex);
void onStartup();
List<String> getNanoWordList(String language);
}
abstract class NanoAccountList {
ObservableList<Account> get accounts;
void update(Object wallet);
void refresh(Object wallet);
List<Account> getAll(Object wallet);
Future<void> addAccount(Object wallet, {required String label});
Future<void> setLabelAccount(Object wallet, {required int accountIndex, required String label});
}
""";
const nanoEmptyDefinition = 'Nano? nano;\n';
const nanoCWDefinition = 'Nano? nano = CWNano();\n';
final output = '$nanoCommonHeaders\n' +
(hasImplementation ? '$nanoCWHeaders\n' : '\n') +
(hasImplementation ? '$nanoCwPart\n\n' : '\n') +
(hasImplementation ? nanoCWDefinition : nanoEmptyDefinition) +
'\n' +
nanoContent;
if (outputFile.existsSync()) {
await outputFile.delete();
}
await outputFile.writeAsString(output);
}
Future<void> generatePubspec({
required bool hasMonero,
required bool hasBitcoin,
required bool hasHaven,
required bool hasEthereum,
required bool hasNano,
required bool hasBanano,
}) async {
const cwCore = """
cw_core:
path: ./cw_core
""";
@ -580,6 +671,14 @@ Future<void> generatePubspec({required bool hasMonero, required bool hasBitcoin,
cw_ethereum:
path: ./cw_ethereum
""";
const cwNano = """
cw_nano:
path: ./cw_nano
""";
const cwBanano = """
cw_nano:
path: ./cw_banano
""";
final inputFile = File(pubspecOutputPath);
final inputText = await inputFile.readAsString();
final inputLines = inputText.split('\n');
@ -604,11 +703,19 @@ Future<void> generatePubspec({required bool hasMonero, required bool hasBitcoin,
output += '\n$cwEthereum';
}
if (hasNano) {
output += '\n$cwNano';
}
if (hasBanano) {
output += '\n$cwBanano';
}
final outputLines = output.split('\n');
inputLines.insertAll(dependenciesIndex + 1, outputLines);
final outputContent = inputLines.join('\n');
final outputFile = File(pubspecOutputPath);
if (outputFile.existsSync()) {
await outputFile.delete();
}
@ -616,9 +723,16 @@ Future<void> generatePubspec({required bool hasMonero, required bool hasBitcoin,
await outputFile.writeAsString(outputContent);
}
Future<void> generateWalletTypes({required bool hasMonero, required bool hasBitcoin, required bool hasHaven, required bool hasEthereum}) async {
Future<void> generateWalletTypes({
required bool hasMonero,
required bool hasBitcoin,
required bool hasHaven,
required bool hasEthereum,
required bool hasNano,
required bool hasBanano,
}) async {
final walletTypesFile = File(walletTypesPath);
if (walletTypesFile.existsSync()) {
await walletTypesFile.delete();
}
@ -647,6 +761,14 @@ Future<void> generateWalletTypes({required bool hasMonero, required bool hasBitc
outputContent += '\tWalletType.haven,\n';
}
if (hasNano) {
outputContent += '\tWalletType.nano,\n';
}
if (hasBanano) {
outputContent += '\tWalletType.banano,\n';
}
outputContent += '];\n';
await walletTypesFile.writeAsString(outputContent);
}
}