mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-04-11 18:01:57 +00:00
added multiple destinations, send all flag; some refactoring
This commit is contained in:
parent
ba2dde7564
commit
43e9cd284a
15 changed files with 216 additions and 369 deletions
|
@ -1,5 +1,5 @@
|
|||
class Destination {
|
||||
final String amount;
|
||||
final int amount; // transfered as string
|
||||
final String address;
|
||||
final String assetId;
|
||||
|
||||
|
@ -7,13 +7,13 @@ class Destination {
|
|||
{required this.amount, required this.address, required this.assetId});
|
||||
|
||||
factory Destination.fromJson(Map<String, dynamic> json) => Destination(
|
||||
amount: json['amount'] as String,
|
||||
amount: int.parse(json['amount'] as String),
|
||||
address: json['address'] as String,
|
||||
assetId: json['asset_id'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"amount": amount,
|
||||
"amount": amount.toString(),
|
||||
"address": address,
|
||||
"asset_id": assetId,
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ class GetWalletInfoResult {
|
|||
GetWalletInfoResult({required this.wi, required this.wiExtended});
|
||||
|
||||
factory GetWalletInfoResult.fromJson(Map<String, dynamic> json) => GetWalletInfoResult(
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>),
|
||||
wiExtended: WiExtended.fromJson(json['wi_extended'] as Map<String, dynamic>));
|
||||
}
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>? ?? {}),
|
||||
wiExtended: WiExtended.fromJson(json['wi_extended'] as Map<String, dynamic>? ?? {}),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@ class GetWalletStatusResult {
|
|||
|
||||
factory GetWalletStatusResult.fromJson(Map<String, dynamic> json) =>
|
||||
GetWalletStatusResult(
|
||||
currentDaemonHeight: json['current_daemon_height'] as int,
|
||||
currentWalletHeight: json['current_wallet_height'] as int,
|
||||
isDaemonConnected: json['is_daemon_connected'] as bool,
|
||||
isInLongRefresh: json['is_in_long_refresh'] as bool,
|
||||
progress: json['progress'] as int,
|
||||
walletState: json['wallet_state'] as int,
|
||||
currentDaemonHeight: json['current_daemon_height'] as int? ?? 0,
|
||||
currentWalletHeight: json['current_wallet_height'] as int? ?? 0,
|
||||
isDaemonConnected: json['is_daemon_connected'] as bool? ?? false,
|
||||
isInLongRefresh: json['is_in_long_refresh'] as bool? ?? false,
|
||||
progress: json['progress'] as int? ?? 0,
|
||||
walletState: json['wallet_state'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -30,12 +30,12 @@ class TransferParams {
|
|||
};
|
||||
|
||||
factory TransferParams.fromJson(Map<String, dynamic> json) => TransferParams(
|
||||
destinations: (json['destinations'] as List<dynamic>).map((e) => Destination.fromJson(e as Map<String, dynamic>)).toList(),
|
||||
fee: json['fee'] as int,
|
||||
mixin: json['mixin'] as int,
|
||||
paymentId: json['payment_id'] as String,
|
||||
comment: json['comment'] as String,
|
||||
pushPayer: json["push_payer"] as bool,
|
||||
hideReceiver: json["hide_receiver"] as bool,
|
||||
destinations: (json['destinations'] as List<dynamic>?)?.map((e) => Destination.fromJson(e as Map<String, dynamic>)).toList() ?? [],
|
||||
fee: json['fee'] as int? ?? 0,
|
||||
mixin: json['mixin'] as int? ?? 0,
|
||||
paymentId: json['payment_id'] as String? ?? '',
|
||||
comment: json['comment'] as String? ?? '',
|
||||
pushPayer: json["push_payer"] as bool? ?? false,
|
||||
hideReceiver: json["hide_receiver"] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ class TransferResult {
|
|||
TransferResult({required this.txHash, required this.txSize, required this.txUnsignedHex});
|
||||
|
||||
factory TransferResult.fromJson(Map<String, dynamic> json) => TransferResult(
|
||||
txHash: json['tx_hash'] as String,
|
||||
txSize: json['tx_size'] as int,
|
||||
txUnsignedHex: json['tx_unsigned_hex'] as String,
|
||||
txHash: json['tx_hash'] as String? ?? '',
|
||||
txSize: json['tx_size'] as int? ?? 0,
|
||||
txUnsignedHex: json['tx_unsigned_hex'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,14 +19,14 @@ class Wi {
|
|||
required this.viewSecKey});
|
||||
|
||||
factory Wi.fromJson(Map<String, dynamic> json) => Wi(
|
||||
address: json['address'] as String,
|
||||
balances: (json['balances'] as List<dynamic>)
|
||||
address: json['address'] as String? ?? '',
|
||||
balances: (json['balances'] as List<dynamic>? ?? [])
|
||||
.map((e) => Balance.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
isAuditable: json['is_auditable'] as bool,
|
||||
isWatchOnly: json['is_watch_only'] as bool,
|
||||
minedTotal: json['mined_total'] as int,
|
||||
path: json['path'] as String,
|
||||
viewSecKey: json['view_sec_key'] as String,
|
||||
isAuditable: json['is_auditable'] as bool? ?? false,
|
||||
isWatchOnly: json['is_watch_only'] as bool? ?? false,
|
||||
minedTotal: json['mined_total'] as int? ?? 0,
|
||||
path: json['path'] as String? ?? '',
|
||||
viewSecKey: json['view_sec_key'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ class WiExtended {
|
|||
WiExtended({required this.seed, required this.spendPrivateKey, required this.spendPublicKey, required this.viewPrivateKey, required this.viewPublicKey});
|
||||
|
||||
factory WiExtended.fromJson(Map<String, dynamic> json) => WiExtended(
|
||||
seed: json["seed"] as String,
|
||||
spendPrivateKey: json["spend_private_key"] as String,
|
||||
spendPublicKey: json["spend_public_key"] as String,
|
||||
viewPrivateKey: json["view_private_key"] as String,
|
||||
viewPublicKey: json["view_public_key"] as String,
|
||||
seed: json["seed"] as String? ?? '',
|
||||
spendPrivateKey: json["spend_private_key"] as String? ?? '',
|
||||
spendPublicKey: json["spend_public_key"] as String? ?? '',
|
||||
viewPrivateKey: json["view_private_key"] as String? ?? '',
|
||||
viewPublicKey: json["view_public_key"] as String? ?? '',
|
||||
);
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_zano/api/api_calls.dart' as calls;
|
||||
import 'package:cw_zano/api/api_calls.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_info_result.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_status_result.dart';
|
||||
import 'package:cw_zano/api/model/zano_wallet_keys.dart';
|
||||
import 'package:cw_zano/zano_balance.dart';
|
||||
import 'package:cw_zano/zano_wallet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart' as mobx;
|
||||
|
||||
int getCurrentHeight(int hWallet) {
|
||||
final json = ApiCalls.getWalletStatus(hWallet: hWallet);
|
||||
final walletStatus = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
return walletStatus.currentWalletHeight;
|
||||
}
|
||||
|
||||
int getNodeHeightSync(int hWallet) {
|
||||
final json = ApiCalls.getWalletStatus(hWallet: hWallet);
|
||||
final walletStatus = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
return walletStatus.currentDaemonHeight;
|
||||
}
|
||||
|
||||
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 hWallet, int baseHeight) async {
|
||||
// if (_cachedBlockchainHeight < baseHeight || _cachedBlockchainHeight == 0) {
|
||||
// _cachedBlockchainHeight = await compute<int, int>(getNodeHeightSync, hWallet);
|
||||
// }
|
||||
|
||||
// return _cachedBlockchainHeight;
|
||||
// }
|
||||
|
||||
void start(ZanoWalletBase wallet, int hWallet) async {
|
||||
_cachedBlockchainHeight = 0;
|
||||
_lastKnownBlockHeight = 0;
|
||||
_initialSyncHeight = 0;
|
||||
_updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async {
|
||||
/**if (isNewTransactionExist()) {
|
||||
onNewTransaction?.call();
|
||||
}*/
|
||||
|
||||
var json = ApiCalls.getWalletStatus(hWallet: hWallet);
|
||||
print('wallet status $json');
|
||||
final status = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
// You can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready)
|
||||
if (!status.isInLongRefresh && status.walletState == 2) {
|
||||
final syncHeight = status.currentWalletHeight;
|
||||
|
||||
json = ApiCalls.getWalletInfo(hWallet);
|
||||
print('wallet info $json');
|
||||
final result = GetWalletInfoResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
wallet.seed = result.wiExtended.seed;
|
||||
wallet.keys = ZanoWalletKeys(
|
||||
privateSpendKey: result.wiExtended.spendPrivateKey,
|
||||
privateViewKey: result.wiExtended.viewPrivateKey,
|
||||
publicSpendKey: result.wiExtended.spendPublicKey,
|
||||
publicViewKey: result.wiExtended.viewPublicKey,
|
||||
);
|
||||
|
||||
final balance = result.wi.balances.first;
|
||||
wallet.assetId = balance.assetInfo.assetId;
|
||||
wallet.balance = mobx.ObservableMap.of({CryptoCurrency.zano: ZanoBalance(total: balance.total, unlocked: balance.unlocked)});
|
||||
|
||||
if (_initialSyncHeight <= 0) {
|
||||
_initialSyncHeight = syncHeight;
|
||||
}
|
||||
|
||||
final bchHeight = status.currentDaemonHeight;
|
||||
|
||||
if (_lastKnownBlockHeight == syncHeight) {
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
class ZanoTransactionCreationException implements Exception {
|
||||
ZanoTransactionCreationException(this.message);
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
|
@ -7,83 +7,62 @@ import 'package:cw_zano/api/model/transfer_result.dart';
|
|||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_zano/api/api_calls.dart' as calls;
|
||||
import 'package:cw_zano/zano_wallet.dart';
|
||||
|
||||
class PendingZanoTransaction with PendingTransaction {
|
||||
PendingZanoTransaction(
|
||||
{required this.zanoWallet,
|
||||
required this.fee,
|
||||
required this.intAmount,
|
||||
//required this.stringAmount,
|
||||
required this.hWallet,
|
||||
required this.address,
|
||||
required this.assetId,
|
||||
required this.comment});
|
||||
PendingZanoTransaction({
|
||||
required this.zanoWallet,
|
||||
required this.destinations,
|
||||
required this.fee,
|
||||
required this.comment,
|
||||
});
|
||||
|
||||
final ZanoWalletBase zanoWallet;
|
||||
final int hWallet;
|
||||
final int intAmount;
|
||||
//final String stringAmount;
|
||||
final List<Destination> destinations;
|
||||
final int fee;
|
||||
final String address;
|
||||
final String assetId;
|
||||
final String comment;
|
||||
|
||||
final CryptoCurrency cryptoCurrency = CryptoCurrency.zano;
|
||||
|
||||
@override
|
||||
String get id => transferResult != null ? transferResult!.txHash : '';
|
||||
String get id => transferResult?.txHash ?? '';
|
||||
|
||||
@override
|
||||
String get hex => '';
|
||||
|
||||
@override
|
||||
String get amountFormatted {
|
||||
return AmountConverter.amountIntToString(cryptoCurrency, intAmount);
|
||||
}
|
||||
String get amountFormatted => AmountConverter.amountIntToString(CryptoCurrency.zano, destinations.first.amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => AmountConverter.amountIntToString(cryptoCurrency, fee);
|
||||
String get feeFormatted => AmountConverter.amountIntToString(CryptoCurrency.zano, fee);
|
||||
|
||||
TransferResult? transferResult;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
final params = TransferParams(
|
||||
destinations: [
|
||||
Destination(
|
||||
amount: intAmount.toString(),
|
||||
address: address,
|
||||
assetId: assetId,
|
||||
)
|
||||
],
|
||||
destinations: destinations,
|
||||
fee: fee,
|
||||
mixin: zanoMixin,
|
||||
paymentId: '',
|
||||
comment: comment,
|
||||
pushPayer: false,
|
||||
hideReceiver: false,
|
||||
hideReceiver: true,
|
||||
);
|
||||
final result = await zanoWallet.invokeMethod(hWallet, 'transfer', params);
|
||||
final result = await zanoWallet.invokeMethod('transfer', params);
|
||||
final map = jsonDecode(result);
|
||||
if (map['result'] != null && map['result']['result'] != null) {
|
||||
transferResult = TransferResult.fromJson(
|
||||
map['result']['result'] as Map<String, dynamic>,
|
||||
);
|
||||
await zanoWallet.fetchTransactions();
|
||||
} else if (map['result'] != null && map['result']['error'] != null) {
|
||||
final String code;
|
||||
if (map['result']['error']['code'] is int) {
|
||||
code = (map['result']['error']['code'] as int).toString();
|
||||
} else if (map['result']['error']['code'] is String) {
|
||||
code = map['result']['error']['code'] as String;
|
||||
final resultMap = map['result'] as Map<String, dynamic>?;
|
||||
if (resultMap != null) {
|
||||
final transferResultMap = resultMap['result'] as Map<String, dynamic>?;
|
||||
if (transferResultMap != null) {
|
||||
transferResult = TransferResult.fromJson(transferResultMap);
|
||||
print('transfer success hash ${transferResult!.txHash}');
|
||||
await zanoWallet.fetchTransactions();
|
||||
} else {
|
||||
code = '';
|
||||
final errorCode = resultMap['error']['code'];
|
||||
final code = errorCode is int ? errorCode.toString() : errorCode as String? ?? '';
|
||||
final message = resultMap['error']['message'] as String? ?? '';
|
||||
print('transfer error $code $message');
|
||||
throw TransferException(code, message);
|
||||
}
|
||||
final message = map['result']['error']['message'] as String;
|
||||
print('transfer error $code $message');
|
||||
throw TransferException(code, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:cw_core/output_info.dart';
|
||||
|
||||
class ZanoTransactionCreationCredentials {
|
||||
ZanoTransactionCreationCredentials(
|
||||
{required this.outputs, required this.priority, required this.assetType});
|
||||
class ZanoTransactionCredentials {
|
||||
ZanoTransactionCredentials({required this.outputs, required this.priority, required this.assetType});
|
||||
|
||||
final List<OutputInfo> outputs;
|
||||
final MoneroTransactionPriority priority;
|
|
@ -5,6 +5,7 @@ import 'dart:io';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
import 'package:cw_core/monero_wallet_utils.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
|
@ -13,17 +14,19 @@ import 'package:cw_core/sync_status.dart';
|
|||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_zano/api/api_calls.dart' as calls;
|
||||
import 'package:cw_zano/api/api_calls.dart';
|
||||
import 'package:cw_zano/api/model/destination.dart';
|
||||
import 'package:cw_zano/api/model/get_recent_txs_and_info_params.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_info_result.dart';
|
||||
import 'package:cw_zano/api/model/get_wallet_status_result.dart';
|
||||
import 'package:cw_zano/api/model/history.dart';
|
||||
import 'package:cw_zano/api/model/store_result.dart';
|
||||
import 'package:cw_zano/api/model/zano_wallet_keys.dart';
|
||||
import 'package:cw_zano/api/wallet.dart' as zano_wallet;
|
||||
import 'package:cw_zano/api/zano_api.dart';
|
||||
import 'package:cw_zano/exceptions/zano_transaction_creation_exception.dart';
|
||||
import 'package:cw_zano/pending_zano_transaction.dart';
|
||||
import 'package:cw_zano/zano_balance.dart';
|
||||
import 'package:cw_zano/zano_transaction_creation_credentials.dart';
|
||||
import 'package:cw_zano/zano_transaction_credentials.dart';
|
||||
import 'package:cw_zano/zano_transaction_history.dart';
|
||||
import 'package:cw_zano/zano_transaction_info.dart';
|
||||
import 'package:cw_zano/zano_wallet_addresses.dart';
|
||||
|
@ -62,7 +65,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
}
|
||||
|
||||
List<History> history = [];
|
||||
String assetId = '';
|
||||
String defaultAsssetId = '';
|
||||
|
||||
static const int _autoSaveInterval = 30;
|
||||
static const _statusDelivered = 'delivered';
|
||||
|
@ -85,8 +88,12 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
@override
|
||||
ZanoWalletKeys keys = ZanoWalletKeys(privateSpendKey: '', privateViewKey: '', publicSpendKey: '', publicViewKey: '');
|
||||
|
||||
zano_wallet.SyncListener? _listener;
|
||||
//zano_wallet.SyncListener? _listener;
|
||||
/**ReactionDisposer? _onAccountChangeReaction;*/
|
||||
Timer? _updateSyncInfoTimer;
|
||||
int _cachedBlockchainHeight = 0;
|
||||
int _lastKnownBlockHeight = 0;
|
||||
int _initialSyncHeight = 0;
|
||||
bool _isTransactionUpdating;
|
||||
bool _hasSyncAfterStartup;
|
||||
Timer? _autoSaveTimer;
|
||||
|
@ -104,17 +111,9 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
await walletAddresses.updateAddress(address);
|
||||
|
||||
///balance.addAll(getZanoBalance(/**accountIndex: walletAddresses.account?.id ?? 0*/));
|
||||
_setListeners();
|
||||
//_setListeners();
|
||||
await updateTransactions();
|
||||
|
||||
if (walletInfo.isRecovery) {
|
||||
debugPrint('setRecoveringFromSeed isRecovery ${walletInfo.isRecovery}');
|
||||
|
||||
if (zano_wallet.getCurrentHeight(hWallet) <= 1) {
|
||||
debugPrint('setRefreshFromBlockHeight height ${walletInfo.restoreHeight}');
|
||||
}
|
||||
}
|
||||
|
||||
_autoSaveTimer = Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
|
||||
}
|
||||
|
||||
|
@ -123,7 +122,8 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
|
||||
@override
|
||||
void close() {
|
||||
_listener?.stop();
|
||||
_updateSyncInfoTimer?.cancel();
|
||||
//_listener?.stop();
|
||||
/**_onAccountChangeReaction?.reaction.dispose();*/
|
||||
_autoSaveTimer?.cancel();
|
||||
}
|
||||
|
@ -151,15 +151,58 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
_setInitialHeight();
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
syncStatus = AttemptingSyncStatus();
|
||||
debugPrint("startRefresh");
|
||||
_setListeners();
|
||||
_listener?.start(this, hWallet);
|
||||
_cachedBlockchainHeight = 0;
|
||||
_lastKnownBlockHeight = 0;
|
||||
_initialSyncHeight = 0;
|
||||
_updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async {
|
||||
/**if (isNewTransactionExist()) {
|
||||
onNewTransaction?.call();
|
||||
}*/
|
||||
|
||||
GetWalletStatusResult status = getWalletStatus();
|
||||
// You can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready)
|
||||
if (!status.isInLongRefresh && status.walletState == 2) {
|
||||
final syncHeight = status.currentWalletHeight;
|
||||
|
||||
GetWalletInfoResult result = getWalletInfo();
|
||||
seed = result.wiExtended.seed;
|
||||
keys = ZanoWalletKeys(
|
||||
privateSpendKey: result.wiExtended.spendPrivateKey,
|
||||
privateViewKey: result.wiExtended.viewPrivateKey,
|
||||
publicSpendKey: result.wiExtended.spendPublicKey,
|
||||
publicViewKey: result.wiExtended.viewPublicKey,
|
||||
);
|
||||
|
||||
final _balance = result.wi.balances.first;
|
||||
defaultAsssetId = _balance.assetInfo.assetId;
|
||||
balance = ObservableMap.of({CryptoCurrency.zano: ZanoBalance(total: _balance.total, unlocked: _balance.unlocked)});
|
||||
|
||||
if (_initialSyncHeight <= 0) {
|
||||
_initialSyncHeight = syncHeight;
|
||||
}
|
||||
|
||||
final bchHeight = status.currentDaemonHeight;
|
||||
|
||||
if (_lastKnownBlockHeight == syncHeight) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
syncStatus = FailedSyncStatus();
|
||||
print(e);
|
||||
|
@ -169,82 +212,54 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
final creds = credentials as ZanoTransactionCreationCredentials;
|
||||
final output = creds.outputs.first;
|
||||
final address = output.isParsedAddress && (output.extractedAddress?.isNotEmpty ?? false) ? output.extractedAddress! : output.address;
|
||||
final stringAmount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
|
||||
final fee = calculateEstimatedFee(creds.priority);
|
||||
final intAmount = (double.parse(stringAmount!) * pow(10, 12)).toInt();
|
||||
final transaction = PendingZanoTransaction(fee: fee, intAmount: intAmount, hWallet: hWallet, address: address, assetId: assetId, comment: output.note ?? '', zanoWallet: this);
|
||||
return transaction;
|
||||
|
||||
/*final _credentials = credentials as ZanoTransactionCreationCredentials;
|
||||
final outputs = _credentials.outputs;
|
||||
final creds = credentials as ZanoTransactionCredentials;
|
||||
final outputs = creds.outputs;
|
||||
final hasMultiDestination = outputs.length > 1;
|
||||
final assetType =
|
||||
CryptoCurrency.fromString(_credentials.assetType.toLowerCase());
|
||||
final balances = getZanoBalance(/*accountIndex: walletAddresses.account!.id*/);
|
||||
final unlockedBalance = balances[assetType]!.unlockedBalance;
|
||||
|
||||
PendingTransactionDescription pendingTransactionDescription;
|
||||
|
||||
if (!(syncStatus is SyncedSyncStatus)) {
|
||||
throw ZanoTransactionCreationException('The wallet is not synced.');
|
||||
}
|
||||
|
||||
final unlockedBalance = balance[CryptoCurrency.zano]?.unlocked ?? 0;
|
||||
final fee = calculateEstimatedFee(creds.priority);
|
||||
late List<Destination> destinations;
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any(
|
||||
(item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
throw ZanoTransactionCreationException(
|
||||
'You do not have enough coins to send this amount.');
|
||||
if (outputs.any((output) => output.sendAll || (output.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
throw ZanoTransactionCreationException("You don't have enough coins.");
|
||||
}
|
||||
|
||||
final int totalAmount = outputs.fold(
|
||||
0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
|
||||
|
||||
if (unlockedBalance < totalAmount) {
|
||||
final int totalAmount = outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
|
||||
if (totalAmount + fee > unlockedBalance) {
|
||||
throw ZanoTransactionCreationException(
|
||||
'You do not have enough coins to send this amount.');
|
||||
"You don't have enough coins (required: ${moneroAmountToString(amount: totalAmount + fee)}, unlocked ${moneroAmountToString(amount: unlockedBalance)}).");
|
||||
}
|
||||
|
||||
final zanoOutputs = outputs
|
||||
.map((output) => ZanoOutput(
|
||||
address: output.address,
|
||||
amount: output.cryptoAmount!.replaceAll(',', '.')))
|
||||
destinations = outputs
|
||||
.map((output) => Destination(
|
||||
amount: output.formattedCryptoAmount ?? 0,
|
||||
address: output.isParsedAddress ? output.extractedAddress! : output.address,
|
||||
assetId: defaultAsssetId,
|
||||
))
|
||||
.toList();
|
||||
|
||||
pendingTransactionDescription =
|
||||
await transaction_history.createTransactionMultDest(
|
||||
outputs: zanoOutputs,
|
||||
priorityRaw: _credentials.priority.serialize());
|
||||
} else {
|
||||
final output = outputs.first;
|
||||
final address = output.isParsedAddress &&
|
||||
(output.extractedAddress?.isNotEmpty ?? false)
|
||||
? output.extractedAddress!
|
||||
: output.address;
|
||||
final amount =
|
||||
output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
|
||||
final int? formattedAmount =
|
||||
output.sendAll ? null : output.formattedCryptoAmount;
|
||||
|
||||
if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
|
||||
(formattedAmount == null && unlockedBalance <= 0)) {
|
||||
final formattedBalance = moneroAmountToString(amount: unlockedBalance);
|
||||
|
||||
throw ZanoTransactionCreationException(
|
||||
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
|
||||
late int amount;
|
||||
if (output.sendAll) {
|
||||
amount = unlockedBalance - fee;
|
||||
} else {
|
||||
amount = output.formattedCryptoAmount!;
|
||||
}
|
||||
|
||||
pendingTransactionDescription =
|
||||
await transaction_history.createTransaction(
|
||||
address: address,
|
||||
assetType: _credentials.assetType,
|
||||
amount: amount,
|
||||
priorityRaw: _credentials.priority.serialize());
|
||||
if (amount + fee > unlockedBalance) {
|
||||
throw ZanoTransactionCreationException(
|
||||
"You don't have enough coins (required: ${moneroAmountToString(amount: amount + fee)}, unlocked ${moneroAmountToString(amount: unlockedBalance)}).");
|
||||
}
|
||||
destinations = [
|
||||
Destination(
|
||||
amount: amount,
|
||||
address: output.isParsedAddress ? output.extractedAddress! : output.address,
|
||||
assetId: defaultAsssetId,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
return PendingZanoTransaction(pendingTransactionDescription, assetType);*/
|
||||
return PendingZanoTransaction(
|
||||
zanoWallet: this,
|
||||
destinations: destinations,
|
||||
fee: fee,
|
||||
comment: outputs.first.note ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -265,7 +280,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
|
||||
Future<void> store() async {
|
||||
try {
|
||||
final json = await invokeMethod(hWallet, 'store', '{}');
|
||||
final json = await invokeMethod('store', '{}');
|
||||
final map = jsonDecode(json) as Map<String, dynamic>;
|
||||
if (map['result'] == null || map['result']['result'] == null) {
|
||||
throw 'store empty response';
|
||||
|
@ -326,7 +341,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
|
||||
Future<void> _refreshTransactions() async {
|
||||
try {
|
||||
final result = await invokeMethod(hWallet, 'get_recent_txs_and_info', GetRecentTxsAndInfoParams(offset: 0, count: 30));
|
||||
final result = await invokeMethod('get_recent_txs_and_info', GetRecentTxsAndInfoParams(offset: 0, count: 30));
|
||||
final map = jsonDecode(result) as Map<String, dynamic>?;
|
||||
if (map == null) {
|
||||
print('get_recent_txs_and_info empty response');
|
||||
|
@ -396,31 +411,14 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
// .map((row) => ZanoTransactionInfo.fromRow(row))
|
||||
// .toList();
|
||||
|
||||
void _setListeners() {
|
||||
_listener?.stop();
|
||||
_listener = zano_wallet.setListeners(_onNewBlock, _onNewTransaction);
|
||||
}
|
||||
// void _setListeners() {
|
||||
// _listener?.stop();
|
||||
// _listener = zano_wallet.setListeners(_onNewBlock, _onNewTransaction);
|
||||
// }
|
||||
|
||||
void _setInitialHeight() {
|
||||
if (walletInfo.isRecovery) {
|
||||
return;
|
||||
}
|
||||
|
||||
final currentHeight = zano_wallet.getCurrentHeight(hWallet);
|
||||
|
||||
if (currentHeight <= 1) {
|
||||
final height = _getHeightByDate(walletInfo.date);
|
||||
debugPrint('setRecoveringFromSeed isRecovery true');
|
||||
debugPrint('setRefreshFromBlockHeight height $height');
|
||||
}
|
||||
}
|
||||
|
||||
int _getHeightByDate(DateTime date) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _askForUpdateBalance() {
|
||||
debugPrint('askForUpdateBalance');
|
||||
debugPrint('askForUpdateBalance'); // TODO: remove, also remove this method completely
|
||||
}
|
||||
|
||||
Future<void> _askForUpdateTransactionHistory() async => await updateTransactions();
|
||||
|
@ -459,7 +457,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
try {
|
||||
await _askForUpdateTransactionHistory();
|
||||
_askForUpdateBalance();
|
||||
await Future<void>.delayed(Duration(seconds: 1));
|
||||
await Future<void>.delayed(Duration(seconds: 1)); // TODO: ???
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
@ -484,7 +482,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
return str;
|
||||
}
|
||||
|
||||
Future<String> invokeMethod(int hWallet, String methodName, Object params) async {
|
||||
Future<String> invokeMethod(String methodName, Object params) async {
|
||||
var invokeResult = ApiCalls.asyncCall(methodName: 'invoke', hWallet: hWallet, params: '{"method": "$methodName","params": ${jsonEncode(params)}}');
|
||||
var map = jsonDecode(invokeResult) as Map<String, dynamic>;
|
||||
int attempts = 0;
|
||||
|
@ -501,4 +499,18 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
}
|
||||
return invokeResult;
|
||||
}
|
||||
|
||||
GetWalletInfoResult getWalletInfo() {
|
||||
final json = ApiCalls.getWalletInfo(hWallet);
|
||||
print('wallet info $json'); // TODO: remove
|
||||
final result = GetWalletInfoResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
return result;
|
||||
}
|
||||
|
||||
GetWalletStatusResult getWalletStatus() {
|
||||
final json = ApiCalls.getWalletStatus(hWallet: hWallet);
|
||||
print('wallet status $json'); // TODO: remove
|
||||
final status = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,11 @@ import 'package:hive/hive.dart';
|
|||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class ZanoNewWalletCredentials extends WalletCredentials {
|
||||
ZanoNewWalletCredentials({required String name, String? password})
|
||||
: super(name: name, password: password);
|
||||
ZanoNewWalletCredentials({required String name, String? password}) : super(name: name, password: password);
|
||||
}
|
||||
|
||||
class ZanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
ZanoRestoreWalletFromSeedCredentials(
|
||||
{required String name, required String password, required int height, required this.mnemonic})
|
||||
ZanoRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required this.mnemonic})
|
||||
: super(name: name, password: password, height: height);
|
||||
|
||||
final String mnemonic;
|
||||
|
@ -38,13 +36,7 @@ class ZanoRestoreWalletFromSeedCredentials extends WalletCredentials {
|
|||
|
||||
class ZanoRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||
ZanoRestoreWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.language,
|
||||
required this.address,
|
||||
required this.viewKey,
|
||||
required this.spendKey,
|
||||
required int height})
|
||||
{required String name, required String password, required this.language, required this.address, required this.viewKey, required this.spendKey, required int height})
|
||||
: super(name: name, password: password, height: height);
|
||||
|
||||
final String language;
|
||||
|
@ -53,14 +45,12 @@ class ZanoRestoreWalletFromKeysCredentials extends WalletCredentials {
|
|||
final String spendKey;
|
||||
}
|
||||
|
||||
class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
|
||||
ZanoRestoreWalletFromSeedCredentials, ZanoRestoreWalletFromKeysCredentials> {
|
||||
class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRestoreWalletFromSeedCredentials, ZanoRestoreWalletFromKeysCredentials> {
|
||||
ZanoWalletService(this.walletInfoSource);
|
||||
|
||||
final Box<WalletInfo> walletInfoSource;
|
||||
|
||||
static bool walletFilesExist(String path) =>
|
||||
!File(path).existsSync() && !File('$path.keys').existsSync();
|
||||
static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync();
|
||||
|
||||
int hWallet = 0;
|
||||
|
||||
|
@ -109,8 +99,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
|
|||
await repairOldAndroidWallet(name);
|
||||
}
|
||||
|
||||
final walletInfo = walletInfoSource.values
|
||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||
final walletInfo = walletInfoSource.values.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||
final wallet = ZanoWallet(walletInfo);
|
||||
await wallet.connectToNode(node: Node());
|
||||
final result = wallet.loadWallet(path, password);
|
||||
|
@ -132,9 +121,8 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
|
|||
wallet.hWallet = hWallet;
|
||||
wallet.walletAddresses.address = result.wi.address;
|
||||
final balance = result.wi.balances.first;
|
||||
wallet.assetId = balance.assetInfo.assetId;
|
||||
wallet.balance = ObservableMap.of(
|
||||
{CryptoCurrency.zano: ZanoBalance(total: balance.total, unlocked: balance.unlocked)});
|
||||
wallet.defaultAsssetId = balance.assetInfo.assetId;
|
||||
wallet.balance = ObservableMap.of({CryptoCurrency.zano: ZanoBalance(total: balance.total, unlocked: balance.unlocked)});
|
||||
if (result.recentHistory.history != null) {
|
||||
wallet.history = result.recentHistory.history!;
|
||||
}
|
||||
|
@ -150,15 +138,13 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
|
|||
await file.delete(recursive: true);
|
||||
}
|
||||
|
||||
final walletInfo = walletInfoSource.values
|
||||
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
|
||||
final walletInfo = walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
|
||||
await walletInfoSource.delete(walletInfo.key);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> rename(String currentName, String password, String newName) async {
|
||||
final currentWalletInfo = walletInfoSource.values
|
||||
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||
final currentWalletInfo = walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||
final currentWallet = ZanoWallet(currentWalletInfo);
|
||||
|
||||
await currentWallet.renameWalletFiles(newName);
|
||||
|
@ -184,8 +170,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
|
|||
final result = ApiCalls.restoreWalletFromSeed(path: path, password: credentials.password!, seed: credentials.mnemonic);
|
||||
final map = json.decode(result) as Map<String, dynamic>;
|
||||
if (map['result'] != null) {
|
||||
final createWalletResult =
|
||||
CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>);
|
||||
final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>);
|
||||
_parseCreateWalletResult(createWalletResult, wallet);
|
||||
await wallet.store();
|
||||
await wallet.init(createWalletResult.wi.address);
|
||||
|
@ -200,7 +185,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
|
|||
}
|
||||
throw RestoreFromSeedException(code, message);
|
||||
}
|
||||
throw RestoreFromSeedException('', '');
|
||||
throw RestoreFromSeedException('', '');
|
||||
} catch (e) {
|
||||
// TODO: Implement Exception for wallet list service.
|
||||
print('ZanoWalletsManager Error: $e');
|
||||
|
|
|
@ -113,36 +113,17 @@ class CWZano extends Zano {
|
|||
|
||||
@override
|
||||
WalletCredentials createZanoRestoreWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String spendKey,
|
||||
required String viewKey,
|
||||
required String address,
|
||||
required String password,
|
||||
required String language,
|
||||
required int height}) {
|
||||
return ZanoRestoreWalletFromKeysCredentials(
|
||||
name: name,
|
||||
spendKey: spendKey,
|
||||
viewKey: viewKey,
|
||||
address: address,
|
||||
password: password,
|
||||
language: language,
|
||||
height: height);
|
||||
{required String name, required String spendKey, required String viewKey, required String address, required String password, required String language, required int height}) {
|
||||
return ZanoRestoreWalletFromKeysCredentials(name: name, spendKey: spendKey, viewKey: viewKey, address: address, password: password, language: language, height: height);
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createZanoRestoreWalletFromSeedCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required int height,
|
||||
required String mnemonic}) {
|
||||
return ZanoRestoreWalletFromSeedCredentials(
|
||||
name: name, password: password, height: height, mnemonic: mnemonic);
|
||||
WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}) {
|
||||
return ZanoRestoreWalletFromSeedCredentials(name: name, password: password, height: height, mnemonic: mnemonic);
|
||||
}
|
||||
|
||||
@override
|
||||
WalletCredentials createZanoNewWalletCredentials(
|
||||
{required String name, String? password}) {
|
||||
WalletCredentials createZanoNewWalletCredentials({required String name, String? password}) {
|
||||
return ZanoNewWalletCredentials(name: name, password: password);
|
||||
}
|
||||
|
||||
|
@ -159,11 +140,8 @@ class CWZano extends Zano {
|
|||
}
|
||||
|
||||
@override
|
||||
Object createZanoTransactionCreationCredentials(
|
||||
{required List<Output> outputs,
|
||||
required TransactionPriority priority,
|
||||
required String assetType}) {
|
||||
return ZanoTransactionCreationCredentials(
|
||||
Object createZanoTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority, required String assetType}) {
|
||||
return ZanoTransactionCredentials(
|
||||
outputs: outputs
|
||||
.map((out) => OutputInfo(
|
||||
fiatAmount: out.fiatAmount,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/utils/language_list.dart';
|
||||
import 'package:cw_zano/zano_transaction_credentials.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/wallet_credentials.dart';
|
||||
|
@ -12,15 +13,12 @@ import 'package:cake_wallet/view_model/send/output.dart';
|
|||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/get_height_by_date.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:cw_zano/zano_wallet_service.dart';
|
||||
import 'package:cw_zano/zano_wallet.dart';
|
||||
import 'package:cw_zano/zano_transaction_info.dart';
|
||||
import 'package:cw_zano/zano_transaction_history.dart';
|
||||
import 'package:cw_zano/mnemonics/english.dart';
|
||||
import 'package:cw_zano/zano_transaction_creation_credentials.dart';
|
||||
|
||||
part 'cw_zano.dart';
|
||||
|
||||
|
|
Loading…
Reference in a new issue