fixed syncing sync status, decimal division, safe null json parsing

This commit is contained in:
leo 2024-03-10 02:51:30 +00:00
parent 75f1f3f7cc
commit 23485a4bab
19 changed files with 232 additions and 151 deletions

View file

@ -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);

View file

@ -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:

View file

@ -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:

View file

@ -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,
);
}

View file

@ -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,
);
}

View file

@ -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>? ?? {}),
);
}

View file

@ -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,
};
}

View file

@ -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,
);
}

View file

@ -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,
);
}

View file

@ -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,
);
}

View file

@ -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,
);
}

View file

@ -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,
);
}

View file

@ -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,
);
}

View file

@ -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,
);
}

View file

@ -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? ?? '',
);
}

View file

@ -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');

View file

@ -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 => '';

View file

@ -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,6 +159,31 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
}
}
void _updateSyncProgress(GetWalletStatusResult walletStatus) {
final syncHeight = walletStatus.currentWalletHeight;
if (_initialSyncHeight <= 0) {
_initialSyncHeight = syncHeight;
}
final bchHeight = walletStatus.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);
}
@override
Future<void> startSync() async {
try {
@ -156,51 +191,31 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
_cachedBlockchainHeight = 0;
_lastKnownBlockHeight = 0;
_initialSyncHeight = 0;
_updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async {
_updateSyncInfoTimer ??=
Timer.periodic(Duration(milliseconds: 1200), (_) async {
/**if (isNewTransactionExist()) {
onNewTransaction?.call();
}*/
GetWalletStatusResult status = getWalletStatus();
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 (!status.isInLongRefresh && status.walletState == 2) {
final syncHeight = status.currentWalletHeight;
GetWalletInfoResult result = getWalletInfo();
seed = result.wiExtended.seed;
if (!walletStatus.isInLongRefresh && walletStatus.walletState == 2) {
final walletInfo = getWalletInfo();
seed = walletInfo.wiExtended.seed;
keys = ZanoWalletKeys(
privateSpendKey: result.wiExtended.spendPrivateKey,
privateViewKey: result.wiExtended.viewPrivateKey,
publicSpendKey: result.wiExtended.spendPublicKey,
publicViewKey: result.wiExtended.viewPublicKey,
privateSpendKey: walletInfo.wiExtended.spendPrivateKey,
privateViewKey: walletInfo.wiExtended.viewPrivateKey,
publicSpendKey: walletInfo.wiExtended.spendPublicKey,
publicViewKey: walletInfo.wiExtended.viewPublicKey,
);
final _balance = result.wi.balances.first;
final _balance = walletInfo.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);
balance = ObservableMap.of({
CryptoCurrency.zano:
ZanoBalance(total: _balance.total, unlocked: _balance.unlocked)
});
}
});
} catch (e) {
@ -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;
}
}

View file

@ -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