mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 03:59:23 +00:00
fixed syncing sync status, decimal division, safe null json parsing
This commit is contained in:
parent
75f1f3f7cc
commit
23485a4bab
19 changed files with 232 additions and 151 deletions
|
@ -1,5 +1,8 @@
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:decimal/intl.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:rational/rational.dart';
|
||||
|
||||
class AmountConverter {
|
||||
static const _moneroAmountLength = 12;
|
||||
|
@ -97,7 +100,7 @@ class AmountConverter {
|
|||
case CryptoCurrency.xusd:
|
||||
return _moneroAmountToString(amount);
|
||||
case CryptoCurrency.zano:
|
||||
return _moneroAmountToString(amount);
|
||||
return _moneroAmountToStringUsingDecimals(amount);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -106,9 +109,16 @@ class AmountConverter {
|
|||
static double cryptoAmountToDouble({required num amount, required num divider}) =>
|
||||
amount / divider;
|
||||
|
||||
static Decimal cryptoAmountToDecimal({required int amount, required int divider}) =>
|
||||
(Decimal.fromInt(amount) / Decimal.fromInt(divider)).toDecimal();
|
||||
|
||||
static String _moneroAmountToString(int amount) => _moneroAmountFormat.format(
|
||||
cryptoAmountToDouble(amount: amount, divider: _moneroAmountDivider));
|
||||
|
||||
static String _moneroAmountToStringUsingDecimals(int amount) => _moneroAmountFormat.format(
|
||||
DecimalIntl(cryptoAmountToDecimal(amount: amount, divider: _moneroAmountDivider)));
|
||||
|
||||
|
||||
static double _moneroAmountToDouble(int amount) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: _moneroAmountDivider);
|
||||
|
||||
|
|
|
@ -177,6 +177,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4"
|
||||
decimal:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: decimal
|
||||
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
encrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -507,6 +515,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
rational:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rational
|
||||
sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -19,6 +19,7 @@ dependencies:
|
|||
flutter_mobx: ^2.0.6+1
|
||||
intl: ^0.18.0
|
||||
encrypt: ^5.0.1
|
||||
decimal: ^2.3.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
@ -21,14 +21,14 @@ class AssetInfo {
|
|||
required this.totalMaxSupply});
|
||||
|
||||
factory AssetInfo.fromJson(Map<String, dynamic> json) => AssetInfo(
|
||||
assetId: json['asset_id'] as String,
|
||||
currentSupply: json['current_supply'] as int,
|
||||
decimalPoint: json['decimal_point'] as int,
|
||||
fullName: json['full_name'] as String,
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
currentSupply: json['current_supply'] as int? ?? 0,
|
||||
decimalPoint: json['decimal_point'] as int? ?? 0,
|
||||
fullName: json['full_name'] as String? ?? '',
|
||||
hiddenSupply: json['hidden_supply'] as bool,
|
||||
metaInfo: json['meta_info'] as String,
|
||||
owner: json['owner'] as String,
|
||||
ticker: json['ticker'] as String,
|
||||
totalMaxSupply: json['total_max_supply'] as int,
|
||||
metaInfo: json['meta_info'] as String? ?? '',
|
||||
owner: json['owner'] as String? ?? '',
|
||||
ticker: json['ticker'] as String? ?? '',
|
||||
totalMaxSupply: json['total_max_supply'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@ class Balance {
|
|||
|
||||
factory Balance.fromJson(Map<String, dynamic> json) => Balance(
|
||||
assetInfo:
|
||||
AssetInfo.fromJson(json['asset_info'] as Map<String, dynamic>),
|
||||
awaitingIn: json['awaiting_in'] as int,
|
||||
awaitingOut: json['awaiting_out'] as int,
|
||||
total: json['total'] as int,
|
||||
unlocked: json['unlocked'] as int,
|
||||
AssetInfo.fromJson(json['asset_info'] as Map<String, dynamic>? ?? {}),
|
||||
awaitingIn: json['awaiting_in'] as int? ?? 0,
|
||||
awaitingOut: json['awaiting_out'] as int? ?? 0,
|
||||
total: json['total'] as int? ?? 0,
|
||||
unlocked: json['unlocked'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,15 +25,15 @@ class CreateWalletResult {
|
|||
|
||||
factory CreateWalletResult.fromJson(Map<String, dynamic> json) =>
|
||||
CreateWalletResult(
|
||||
name: json['name'] as String,
|
||||
pass: json['pass'] as String,
|
||||
name: json['name'] as String? ?? '',
|
||||
pass: json['pass'] as String? ?? '',
|
||||
recentHistory: RecentHistory.fromJson(
|
||||
json['recent_history'] as Map<String, dynamic>),
|
||||
recovered: json['recovered'] as bool,
|
||||
seed: json['seed'] as String,
|
||||
walletFileSize: json['wallet_file_size'] as int,
|
||||
walletId: json['wallet_id'] as int,
|
||||
walletLocalBcSize: json['wallet_local_bc_size'] as int,
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>),
|
||||
json['recent_history'] as Map<String, dynamic>? ?? {}),
|
||||
recovered: json['recovered'] as bool? ?? false,
|
||||
seed: json['seed'] as String? ?? '',
|
||||
walletFileSize: json['wallet_file_size'] as int? ?? 0,
|
||||
walletId: json['wallet_id'] as int? ?? 0,
|
||||
walletLocalBcSize: json['wallet_local_bc_size'] as int? ?? 0,
|
||||
wi: Wi.fromJson(json['wi'] as Map<String, dynamic>? ?? {}),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,14 +7,14 @@ class Destination {
|
|||
{required this.amount, required this.address, required this.assetId});
|
||||
|
||||
factory Destination.fromJson(Map<String, dynamic> json) => Destination(
|
||||
amount: int.parse(json['amount'] as String),
|
||||
address: json['address'] as String,
|
||||
assetId: json['asset_id'] as String,
|
||||
amount: int.parse(json['amount'] as String? ?? '0'),
|
||||
address: json['address'] as String? ?? '',
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"amount": amount.toString(),
|
||||
"address": address,
|
||||
"asset_id": assetId,
|
||||
'amount': amount.toString(),
|
||||
'address': address,
|
||||
'asset_id': assetId,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ class GetAddressInfoResult {
|
|||
{required this.valid, required this.auditable, required this.paymentId, required this.wrap});
|
||||
|
||||
factory GetAddressInfoResult.fromJson(Map<String, dynamic> json) => GetAddressInfoResult(
|
||||
valid: json['valid'] as bool,
|
||||
auditable: json['auditable'] as bool,
|
||||
paymentId: json['payment_id'] as bool,
|
||||
wrap: json['wrap'] as bool,
|
||||
valid: json['valid'] as bool? ?? false,
|
||||
auditable: json['auditable'] as bool? ?? false,
|
||||
paymentId: json['payment_id'] as bool? ?? false,
|
||||
wrap: json['wrap'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -43,27 +43,27 @@ class History {
|
|||
});
|
||||
|
||||
factory History.fromJson(Map<String, dynamic> json) => History(
|
||||
comment: json['comment'] as String,
|
||||
comment: json['comment'] as String? ?? '',
|
||||
employedEntries: EmployedEntries.fromJson(
|
||||
json['employed_entries'] as Map<String, dynamic>),
|
||||
fee: json['fee'] as int,
|
||||
height: json['height'] as int,
|
||||
isMining: json['is_mining'] as bool,
|
||||
isMixing: json['is_mixing'] as bool,
|
||||
isService: json['is_service'] as bool,
|
||||
paymentId: json['payment_id'] as String,
|
||||
json['employed_entries'] as Map<String, dynamic>? ?? {}),
|
||||
fee: json['fee'] as int? ?? 0,
|
||||
height: json['height'] as int? ?? 0,
|
||||
isMining: json['is_mining'] as bool? ?? false,
|
||||
isMixing: json['is_mixing'] as bool? ?? false,
|
||||
isService: json['is_service'] as bool? ?? false,
|
||||
paymentId: json['payment_id'] as String? ?? '',
|
||||
remoteAddresses: json['remote_addresses'] == null ? [] :
|
||||
(json['remote_addresses'] as List<dynamic>).cast<String>(),
|
||||
remoteAliases: json['remote_aliases'] == null ? [] : (json['remote_aliases'] as List<dynamic>).cast<String>(),
|
||||
showSender: json['show_sender'] as bool,
|
||||
subtransfers: (json['subtransfers'] as List<dynamic>)
|
||||
showSender: json['show_sender'] as bool? ?? false,
|
||||
subtransfers: (json['subtransfers'] as List<dynamic>? ?? [])
|
||||
.map((e) => Subtransfer.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
timestamp: json['timestamp'] as int,
|
||||
transferInternalIndex: json['transfer_internal_index'] is double ? (json['transfer_internal_index'] as double).toInt() : json['transfer_internal_index'] as int,
|
||||
txBlobSize: json['tx_blob_size'] as int,
|
||||
txHash: json['tx_hash'] as String,
|
||||
txType: json['tx_type'] as int,
|
||||
unlockTime: json['unlock_time'] as int,
|
||||
timestamp: json['timestamp'] as int? ?? 0,
|
||||
transferInternalIndex: json['transfer_internal_index'] == null ? 0 : json['transfer_internal_index'] is double ? (json['transfer_internal_index'] as double).toInt() : json['transfer_internal_index'] as int,
|
||||
txBlobSize: json['tx_blob_size'] as int? ?? 0,
|
||||
txHash: json['tx_hash'] as String? ?? '',
|
||||
txType: json['tx_type'] as int? ?? 0,
|
||||
unlockTime: json['unlock_time'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ class Receive {
|
|||
Receive({required this.amount, required this.assetId, required this.index});
|
||||
|
||||
factory Receive.fromJson(Map<String, dynamic> json) => Receive(
|
||||
amount: json['amount'] as int,
|
||||
assetId: json['asset_id'] as String,
|
||||
index: json['index'] as int,
|
||||
amount: json['amount'] as int? ?? 0,
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
index: json['index'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class RecentHistory {
|
|||
history: json['history'] == null ? null : (json['history'] as List<dynamic>)
|
||||
.map((e) => History.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
lastItemIndex: json['last_item_index'] as int,
|
||||
totalHistoryItems: json['total_history_items'] as int,
|
||||
lastItemIndex: json['last_item_index'] as int? ?? 0,
|
||||
totalHistoryItems: json['total_history_items'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ class StoreResult {
|
|||
StoreResult({required this.walletFileSize});
|
||||
|
||||
factory StoreResult.fromJson(Map<String, dynamic> json) => StoreResult(
|
||||
walletFileSize: json['wallet_file_size'] as int,
|
||||
walletFileSize: json['wallet_file_size'] as int? ?? 0,
|
||||
);
|
||||
}
|
|
@ -7,8 +7,8 @@ class Subtransfer {
|
|||
{required this.amount, required this.assetId, required this.isIncome});
|
||||
|
||||
factory Subtransfer.fromJson(Map<String, dynamic> json) => Subtransfer(
|
||||
amount: json['amount'] as int,
|
||||
assetId: json['asset_id'] as String,
|
||||
isIncome: json['is_income'] as bool,
|
||||
amount: json['amount'] as int? ?? 0,
|
||||
assetId: json['asset_id'] as String? ?? '',
|
||||
isIncome: json['is_income'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ class TransferParams {
|
|||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"destinations": destinations,
|
||||
"fee": fee,
|
||||
"mixin": mixin,
|
||||
"payment_id": paymentId,
|
||||
"comment": comment,
|
||||
"push_payer": pushPayer,
|
||||
"hide_receiver": hideReceiver,
|
||||
'destinations': destinations,
|
||||
'fee': fee,
|
||||
'mixin': mixin,
|
||||
'payment_id': paymentId,
|
||||
'comment': comment,
|
||||
'push_payer': pushPayer,
|
||||
'hide_receiver': hideReceiver,
|
||||
};
|
||||
|
||||
factory TransferParams.fromJson(Map<String, dynamic> json) => TransferParams(
|
||||
|
@ -35,7 +35,7 @@ class TransferParams {
|
|||
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,
|
||||
pushPayer: json['push_payer'] as bool? ?? false,
|
||||
hideReceiver: json['hide_receiver'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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? ?? '',
|
||||
);
|
||||
}
|
|
@ -2,5 +2,5 @@ import 'dart:ffi';
|
|||
import 'dart:io';
|
||||
|
||||
final DynamicLibrary zanoApi = Platform.isAndroid
|
||||
? DynamicLibrary.open("libcw_zano.so")
|
||||
: DynamicLibrary.open("cw_zano.framework/cw_zano");
|
||||
? DynamicLibrary.open('libcw_zano.so')
|
||||
: DynamicLibrary.open('cw_zano.framework/cw_zano');
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
|
||||
class ZanoBalance extends Balance {
|
||||
|
@ -7,10 +9,10 @@ class ZanoBalance extends Balance {
|
|||
ZanoBalance({required this.total, required this.unlocked}): super(unlocked, total-unlocked);
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance => moneroAmountToString(amount: total-unlocked);
|
||||
String get formattedAdditionalBalance => AmountConverter.amountIntToString(CryptoCurrency.zano, total-unlocked);
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance => moneroAmountToString(amount: unlocked);
|
||||
String get formattedAvailableBalance => AmountConverter.amountIntToString(CryptoCurrency.zano, unlocked);
|
||||
|
||||
@override
|
||||
String get formattedFrozenBalance => '';
|
||||
|
|
|
@ -40,28 +40,32 @@ const moneroBlockSize = 1000;
|
|||
|
||||
class ZanoWallet = ZanoWalletBase with _$ZanoWallet;
|
||||
|
||||
typedef _load_wallet = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
|
||||
typedef _load_wallet = Pointer<Utf8> Function(
|
||||
Pointer<Utf8>, Pointer<Utf8>, Int8);
|
||||
typedef _LoadWallet = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>, int);
|
||||
|
||||
const int zanoMixin = 10;
|
||||
|
||||
abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHistory, ZanoTransactionInfo> with Store {
|
||||
abstract class ZanoWalletBase
|
||||
extends WalletBase<ZanoBalance, ZanoTransactionHistory, ZanoTransactionInfo>
|
||||
with Store {
|
||||
ZanoWalletBase(WalletInfo walletInfo)
|
||||
: balance = ObservableMap.of({CryptoCurrency.zano: ZanoBalance(total: 0, unlocked: 0)}),
|
||||
: balance = ObservableMap.of(
|
||||
{CryptoCurrency.zano: ZanoBalance(total: 0, unlocked: 0)}),
|
||||
_isTransactionUpdating = false,
|
||||
_hasSyncAfterStartup = false,
|
||||
walletAddresses = ZanoWalletAddresses(walletInfo),
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
super(walletInfo) {
|
||||
transactionHistory = ZanoTransactionHistory();
|
||||
/*_onAccountChangeReaction =
|
||||
reaction((_) => walletAddresses.account, (Account? account) {
|
||||
if (account == null) {
|
||||
return;
|
||||
}
|
||||
balance.addAll(getZanoBalance(accountIndex: account.id));
|
||||
/**walletAddresses.updateSubaddressList(accountIndex: account.id);*/
|
||||
});*/
|
||||
// _onAccountChangeReaction =
|
||||
// reaction((_) => walletAddresses.account, (Account? account) {
|
||||
// if (account == null) {
|
||||
// return;
|
||||
// }
|
||||
// balance.addAll(getZanoBalance(accountIndex: account.id));
|
||||
// /**walletAddresses.updateSubaddressList(accountIndex: account.id);*/
|
||||
// });
|
||||
}
|
||||
|
||||
List<History> history = [];
|
||||
|
@ -86,10 +90,14 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
String seed = '';
|
||||
|
||||
@override
|
||||
ZanoWalletKeys keys = ZanoWalletKeys(privateSpendKey: '', privateViewKey: '', publicSpendKey: '', publicViewKey: '');
|
||||
ZanoWalletKeys keys = ZanoWalletKeys(
|
||||
privateSpendKey: '',
|
||||
privateViewKey: '',
|
||||
publicSpendKey: '',
|
||||
publicViewKey: '');
|
||||
|
||||
//zano_wallet.SyncListener? _listener;
|
||||
/**ReactionDisposer? _onAccountChangeReaction;*/
|
||||
// ReactionDisposer? _onAccountChangeReaction;
|
||||
Timer? _updateSyncInfoTimer;
|
||||
int _cachedBlockchainHeight = 0;
|
||||
int _lastKnownBlockHeight = 0;
|
||||
|
@ -114,7 +122,8 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
//_setListeners();
|
||||
await updateTransactions();
|
||||
|
||||
_autoSaveTimer = Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
|
||||
_autoSaveTimer = Timer.periodic(
|
||||
Duration(seconds: _autoSaveInterval), (_) async => await save());
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -122,9 +131,10 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
|
||||
@override
|
||||
void close() {
|
||||
ApiCalls.closeWallet(hWallet: hWallet);
|
||||
_updateSyncInfoTimer?.cancel();
|
||||
//_listener?.stop();
|
||||
/**_onAccountChangeReaction?.reaction.dispose();*/
|
||||
// _onAccountChangeReaction?.reaction.dispose();
|
||||
_autoSaveTimer?.cancel();
|
||||
}
|
||||
|
||||
|
@ -133,9 +143,9 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
try {
|
||||
syncStatus = ConnectingSyncStatus();
|
||||
await ApiCalls.setupNode(
|
||||
address: "195.201.107.230:33336", // node.uriRaw,
|
||||
login: "", // node.login,
|
||||
password: "", // node.password,
|
||||
address: '195.201.107.230:33336', // node.uriRaw,
|
||||
login: '', // node.login,
|
||||
password: '', // node.password,
|
||||
useSSL: false, // node.useSSL ?? false,
|
||||
isLightWallet: false, // FIXME: hardcoded value
|
||||
/*socksProxyAddress: node.socksProxyAddress*/
|
||||
|
@ -149,41 +159,12 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = AttemptingSyncStatus();
|
||||
_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)});
|
||||
|
||||
void _updateSyncProgress(GetWalletStatusResult walletStatus) {
|
||||
final syncHeight = walletStatus.currentWalletHeight;
|
||||
if (_initialSyncHeight <= 0) {
|
||||
_initialSyncHeight = syncHeight;
|
||||
}
|
||||
|
||||
final bchHeight = status.currentDaemonHeight;
|
||||
final bchHeight = walletStatus.currentDaemonHeight;
|
||||
|
||||
if (_lastKnownBlockHeight == syncHeight) {
|
||||
return;
|
||||
|
@ -202,6 +183,40 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
// 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents;
|
||||
_onNewBlock.call(syncHeight, left, ptc);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = AttemptingSyncStatus();
|
||||
_cachedBlockchainHeight = 0;
|
||||
_lastKnownBlockHeight = 0;
|
||||
_initialSyncHeight = 0;
|
||||
_updateSyncInfoTimer ??=
|
||||
Timer.periodic(Duration(milliseconds: 1200), (_) async {
|
||||
/**if (isNewTransactionExist()) {
|
||||
onNewTransaction?.call();
|
||||
}*/
|
||||
|
||||
final walletStatus = getWalletStatus();
|
||||
_updateSyncProgress(walletStatus);
|
||||
// You can call getWalletInfo ONLY if getWalletStatus returns NOT is in long refresh and wallet state is 2 (ready)
|
||||
if (!walletStatus.isInLongRefresh && walletStatus.walletState == 2) {
|
||||
final walletInfo = getWalletInfo();
|
||||
seed = walletInfo.wiExtended.seed;
|
||||
keys = ZanoWalletKeys(
|
||||
privateSpendKey: walletInfo.wiExtended.spendPrivateKey,
|
||||
privateViewKey: walletInfo.wiExtended.viewPrivateKey,
|
||||
publicSpendKey: walletInfo.wiExtended.spendPublicKey,
|
||||
publicViewKey: walletInfo.wiExtended.viewPublicKey,
|
||||
);
|
||||
|
||||
final _balance = walletInfo.wi.balances.first;
|
||||
defaultAsssetId = _balance.assetInfo.assetId;
|
||||
balance = ObservableMap.of({
|
||||
CryptoCurrency.zano:
|
||||
ZanoBalance(total: _balance.total, unlocked: _balance.unlocked)
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
syncStatus = FailedSyncStatus();
|
||||
|
@ -219,10 +234,12 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
final fee = calculateEstimatedFee(creds.priority);
|
||||
late List<Destination> destinations;
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any((output) => output.sendAll || (output.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
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));
|
||||
final int totalAmount = outputs.fold(
|
||||
0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
|
||||
if (totalAmount + fee > unlockedBalance) {
|
||||
throw ZanoTransactionCreationException(
|
||||
"You don't have enough coins (required: ${moneroAmountToString(amount: totalAmount + fee)}, unlocked ${moneroAmountToString(amount: unlockedBalance)}).");
|
||||
|
@ -230,7 +247,9 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
destinations = outputs
|
||||
.map((output) => Destination(
|
||||
amount: output.formattedCryptoAmount ?? 0,
|
||||
address: output.isParsedAddress ? output.extractedAddress! : output.address,
|
||||
address: output.isParsedAddress
|
||||
? output.extractedAddress!
|
||||
: output.address,
|
||||
assetId: defaultAsssetId,
|
||||
))
|
||||
.toList();
|
||||
|
@ -249,13 +268,16 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
destinations = [
|
||||
Destination(
|
||||
amount: amount,
|
||||
address: output.isParsedAddress ? output.extractedAddress! : output.address,
|
||||
address: output.isParsedAddress
|
||||
? output.extractedAddress!
|
||||
: output.address,
|
||||
assetId: defaultAsssetId,
|
||||
)
|
||||
];
|
||||
}
|
||||
destinations.forEach((destination) {
|
||||
debugPrint('destination ${destination.address} ${destination.amount} ${destination.assetId}');
|
||||
debugPrint(
|
||||
'destination ${destination.address} ${destination.amount} ${destination.assetId}');
|
||||
});
|
||||
return PendingZanoTransaction(
|
||||
zanoWallet: this,
|
||||
|
@ -266,7 +288,8 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
}
|
||||
|
||||
@override
|
||||
int calculateEstimatedFee(TransactionPriority priority, [int? amount = null]) {
|
||||
int calculateEstimatedFee(TransactionPriority priority,
|
||||
[int? amount = null]) {
|
||||
return ApiCalls.getCurrentTxFee(priority: priority.raw);
|
||||
}
|
||||
|
||||
|
@ -288,7 +311,8 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
if (map['result'] == null || map['result']['result'] == null) {
|
||||
throw 'store empty response';
|
||||
}
|
||||
final _ = StoreResult.fromJson(map['result']['result'] as Map<String, dynamic>);
|
||||
final _ =
|
||||
StoreResult.fromJson(map['result']['result'] as Map<String, dynamic>);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
@ -344,7 +368,8 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
|
||||
Future<void> _refreshTransactions() async {
|
||||
try {
|
||||
final result = await invokeMethod('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');
|
||||
|
@ -368,7 +393,9 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
return;
|
||||
}
|
||||
|
||||
history = transfers.map((e) => History.fromJson(e as Map<String, dynamic>)).toList();
|
||||
history = transfers
|
||||
.map((e) => History.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
@ -378,7 +405,10 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
Future<Map<String, ZanoTransactionInfo>> fetchTransactions() async {
|
||||
try {
|
||||
await _refreshTransactions();
|
||||
return history.map<ZanoTransactionInfo>((history) => ZanoTransactionInfo.fromHistory(history)).fold<Map<String, ZanoTransactionInfo>>(
|
||||
return history
|
||||
.map<ZanoTransactionInfo>(
|
||||
(history) => ZanoTransactionInfo.fromHistory(history))
|
||||
.fold<Map<String, ZanoTransactionInfo>>(
|
||||
<String, ZanoTransactionInfo>{},
|
||||
(Map<String, ZanoTransactionInfo> acc, ZanoTransactionInfo tx) {
|
||||
acc[tx.id] = tx;
|
||||
|
@ -420,10 +450,12 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
// }
|
||||
|
||||
void _askForUpdateBalance() {
|
||||
debugPrint('askForUpdateBalance'); // TODO: remove, also remove this method completely
|
||||
debugPrint(
|
||||
'askForUpdateBalance'); // TODO: remove, also remove this method completely
|
||||
}
|
||||
|
||||
Future<void> _askForUpdateTransactionHistory() async => await updateTransactions();
|
||||
Future<void> _askForUpdateTransactionHistory() async =>
|
||||
await updateTransactions();
|
||||
|
||||
void _onNewBlock(int height, int blocksLeft, double ptc) async {
|
||||
try {
|
||||
|
@ -473,7 +505,10 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
}
|
||||
|
||||
Future<String> invokeMethod(String methodName, Object params) async {
|
||||
var invokeResult = ApiCalls.asyncCall(methodName: 'invoke', hWallet: hWallet, params: '{"method": "$methodName","params": ${jsonEncode(params)}}');
|
||||
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;
|
||||
if (map['job_id'] != null) {
|
||||
|
@ -482,7 +517,9 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
await Future.delayed(Duration(milliseconds: attempts < 2 ? 100 : 500));
|
||||
final result = ApiCalls.tryPullResult(jobId);
|
||||
map = jsonDecode(result) as Map<String, dynamic>;
|
||||
if (map['status'] != null && map['status'] == _statusDelivered && map['result'] != null) {
|
||||
if (map['status'] != null &&
|
||||
map['status'] == _statusDelivered &&
|
||||
map['result'] != null) {
|
||||
return result;
|
||||
}
|
||||
} while (++attempts < _maxAttempts);
|
||||
|
@ -493,14 +530,16 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
|
|||
GetWalletInfoResult getWalletInfo() {
|
||||
final json = ApiCalls.getWalletInfo(hWallet);
|
||||
print('wallet info $json'); // TODO: remove
|
||||
final result = GetWalletInfoResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
|
||||
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>);
|
||||
final status = GetWalletStatusResult.fromJson(
|
||||
jsonDecode(json) as Map<String, dynamic>);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,9 +63,9 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
|
|||
final wallet = ZanoWallet(credentials.walletInfo!);
|
||||
await wallet.connectToNode(node: Node());
|
||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||
final result = ApiCalls.createWallet(language: "", path: path, password: credentials.password!);
|
||||
final result = ApiCalls.createWallet(language: '', path: path, password: credentials.password!);
|
||||
final map = json.decode(result) as Map<String, dynamic>;
|
||||
if (map['result'] == null) throw CreateWalletException('');
|
||||
_checkForCreateWalletError(map);
|
||||
final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>);
|
||||
_parseCreateWalletResult(createWalletResult, wallet);
|
||||
await wallet.store();
|
||||
|
@ -103,9 +103,9 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
|
|||
final wallet = ZanoWallet(walletInfo);
|
||||
await wallet.connectToNode(node: Node());
|
||||
final result = wallet.loadWallet(path, password);
|
||||
print("load wallet result $result");
|
||||
print('load wallet result $result');
|
||||
final map = json.decode(result) as Map<String, dynamic>;
|
||||
if (map['result'] == null) throw CreateWalletException('');
|
||||
_checkForCreateWalletError(map);
|
||||
final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>);
|
||||
_parseCreateWalletResult(createWalletResult, wallet);
|
||||
await wallet.store();
|
||||
|
@ -113,6 +113,19 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
|
|||
return wallet;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
// TODO: uncomment after merge
|
||||
//await restoreWalletFilesFromBackup(name);
|
||||
}
|
||||
}
|
||||
|
||||
void _checkForCreateWalletError(Map<String, dynamic> map) {
|
||||
if (map['error'] != null) {
|
||||
final code = map['error']!['code'] ?? '';
|
||||
final message = map['error']!['message'] ?? '';
|
||||
throw CreateWalletException('Error creating/loading wallet $code $message');
|
||||
}
|
||||
if (map['result'] == null) {
|
||||
throw CreateWalletException('Error creating/loading wallet, empty response');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +171,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
|
|||
|
||||
@override
|
||||
Future<ZanoWallet> restoreFromKeys(ZanoRestoreWalletFromKeysCredentials credentials) async {
|
||||
throw UnimplementedError("Restore from keys not implemented");
|
||||
throw UnimplementedError('Restore from keys not implemented');
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in a new issue