changed fields to BigInt, some fixes

This commit is contained in:
leo 2024-08-07 12:32:47 +00:00
parent e7b29c0a56
commit 02bfe643d9
26 changed files with 262 additions and 455 deletions

View file

@ -1,5 +1,6 @@
import 'dart:ffi';
import 'package:cw_zano/api/utf8.dart';
import 'package:cw_zano/api/utf8_box.dart';
import 'package:cw_zano/api/zano_api.dart';
import 'package:ffi/ffi.dart';
@ -52,12 +53,13 @@ typedef _SetPassword = Pointer<Utf8> Function(int hWallet, Pointer<Utf8> passwor
// char* get_connectivity_status()
// char* get_version()
// get_opened_wallets()
typedef _stringFunction = Pointer<Utf8> Function();
class ApiCalls {
static String _convertUTF8ToString({required Pointer<Utf8> pointer}) {
final str = pointer.toDartString();
//final str = pointer.toDartStringAllowingMalformed();
//final str = pointer.toDartString();
final str = pointer.toDartStringAllowingMalformed();
calloc.free(pointer);
return str;
}
@ -184,6 +186,9 @@ class ApiCalls {
static String getConnectivityStatus() => _performApiCall(() => _getConnectivityStatusNative());
static final _getOpenedWalletsNative = zanoApi.lookup<NativeFunction<_stringFunction>>('get_opened_wallets').asFunction<_stringFunction>();
static String getOpenedWallets() => _performApiCall(() => _getOpenedWalletsNative());
static final _getAddressInfoNative = zanoApi.lookup<NativeFunction<_GetAddressInfo>>('get_address_info').asFunction<_GetAddressInfo>();
static String getAddressInfo({required String address}) {

View file

@ -2,4 +2,5 @@ class Consts {
static const errorWrongSeed = 'WRONG_SEED';
static const errorAlreadyExists = 'ALREADY_EXISTS';
static const errorWalletWrongId = 'WALLET_WRONG_ID';
static const errorBusy = 'BUSY';
}

View file

@ -1,11 +1,12 @@
import 'package:cw_zano/model/zano_asset.dart';
import 'package:cw_zano/zano_formatter.dart';
class Balance {
final ZanoAsset assetInfo;
final int awaitingIn;
final int awaitingOut;
final int total;
final int unlocked;
final BigInt awaitingIn;
final BigInt awaitingOut;
final BigInt total;
final BigInt unlocked;
Balance(
{required this.assetInfo,
@ -22,9 +23,9 @@ class Balance {
factory Balance.fromJson(Map<String, dynamic> json) => Balance(
assetInfo:
ZanoAsset.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,
awaitingIn: ZanoFormatter.bigIntFromDynamic(json['awaiting_in']),
awaitingOut: ZanoFormatter.bigIntFromDynamic(json['awaiting_out']),
total: ZanoFormatter.bigIntFromDynamic(json['total']),
unlocked: ZanoFormatter.bigIntFromDynamic(json['unlocked']),
);
}

View file

@ -1,12 +1,14 @@
import 'package:cw_zano/zano_formatter.dart';
class Receive {
final int amount;
final BigInt amount;
final String assetId;
final int index;
Receive({required this.amount, required this.assetId, required this.index});
factory Receive.fromJson(Map<String, dynamic> json) => Receive(
amount: json['amount'] as int? ?? 0,
amount: ZanoFormatter.bigIntFromDynamic(json['amount']),
assetId: json['asset_id'] as String? ?? '',
index: json['index'] as int? ?? 0,
);

View file

@ -1,5 +1,7 @@
import 'package:cw_zano/zano_formatter.dart';
class Subtransfer {
final int amount;
final BigInt amount;
final String assetId;
final bool isIncome;
@ -7,7 +9,7 @@ class Subtransfer {
{required this.amount, required this.assetId, required this.isIncome});
factory Subtransfer.fromJson(Map<String, dynamic> json) => Subtransfer(
amount: json['amount'] as int? ?? 0,
amount: ZanoFormatter.bigIntFromDynamic(json['amount']),
assetId: json['asset_id'] as String? ?? '',
isIncome: json['is_income'] as bool? ?? false,
);

View file

@ -3,7 +3,9 @@ import 'package:cw_zano/api/model/subtransfer.dart';
import 'package:collection/collection.dart';
import 'package:cw_zano/model/zano_asset.dart';
import 'package:cw_zano/model/zano_transaction_info.dart';
import 'package:cw_zano/zano_formatter.dart';
import 'package:cw_zano/zano_wallet.dart';
import 'package:cw_zano/zano_wallet_api.dart';
class Transfer {
final String comment;
@ -71,8 +73,8 @@ class Transfer {
unlockTime: json['unlock_time'] as int? ?? 0,
);
static Map<String, ZanoTransactionInfo> makeMap(List<Transfer> transfers, Map<String, ZanoAsset> zanoAssets, int currentDaemonHeight) =>
Map.fromIterable(
static Map<String, ZanoTransactionInfo> makeMap(List<Transfer> transfers, Map<String, ZanoAsset> zanoAssets, int currentDaemonHeight) {
return Map.fromIterable(
transfers,
key: (item) => (item as Transfer).txHash,
value: (transfer) {
@ -81,7 +83,7 @@ class Transfer {
Subtransfer? single = transfer.subtransfers.singleOrNull;
if (transfer.subtransfers.length == 2) {
final zano = transfer.subtransfers.firstWhereOrNull((element) => element.assetId == ZanoWalletBase.zanoAssetId);
if (zano != null && !zano.isIncome && zano.amount == transfer.fee) {
if (zano != null && !zano.isIncome && zano.amount == BigInt.from(transfer.fee)) {
single = transfer.subtransfers.firstWhere((element) => element.assetId != ZanoWalletBase.zanoAssetId);
}
}
@ -93,18 +95,22 @@ class Transfer {
}
if (single.assetId != ZanoWalletBase.zanoAssetId) {
final asset = zanoAssets[single.assetId];
if (asset != null)
if (asset == null) {
ZanoWalletApi.error('unknown asset ${single.assetId}');
}
final ticker = asset == null ? '***' : asset.ticker;
final decimalPoint = asset == null ? ZanoFormatter.defaultDecimalPoint : asset.decimalPoint;
return ZanoTransactionInfo.fromTransfer(
transfer,
confirmations: currentDaemonHeight - transfer.height,
isIncome: single.isIncome,
assetId: single.assetId,
amount: single.amount,
tokenSymbol: isSimple ? asset.ticker : '*${asset.ticker}',
decimalPoint: asset.decimalPoint,
tokenSymbol: isSimple ? ticker : '*${ticker}',
decimalPoint: decimalPoint,
);
}
final amount = single.isIncome ? single.amount : single.amount - transfer.fee;
final amount = single.isIncome ? single.amount : single.amount - BigInt.from(transfer.fee);
return ZanoTransactionInfo.fromTransfer(
transfer,
confirmations: currentDaemonHeight - transfer.height,
@ -115,4 +121,5 @@ class Transfer {
);
},
);
}
}

View file

@ -26,11 +26,11 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
@HiveField(7)
final String metaInfo;
@HiveField(8)
final int currentSupply;
final BigInt currentSupply;
@HiveField(9)
final bool hiddenSupply;
@HiveField(10)
final int totalMaxSupply;
final BigInt totalMaxSupply;
@HiveField(11)
final bool isInGlobalWhitelist;
@ -47,9 +47,9 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
this.iconPath,
this.owner = defaultOwner,
this.metaInfo = '',
this.currentSupply = 0,
required this.currentSupply,
this.hiddenSupply = false,
this.totalMaxSupply = 0,
required this.totalMaxSupply,
this.isInGlobalWhitelist = false,
}) : _enabled = enabled,
super(
@ -86,17 +86,19 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
factory ZanoAsset.fromJson(Map<String, dynamic> json, {bool isInGlobalWhitelist = false}) => ZanoAsset(
assetId: json['asset_id'] as String? ?? '',
currentSupply: json['current_supply'] as int? ?? 0,
currentSupply: ZanoFormatter.bigIntFromDynamic(json['current_supply']),
decimalPoint: json['decimal_point'] as int? ?? ZanoFormatter.defaultDecimalPoint,
fullName: json['full_name'] as String? ?? '',
hiddenSupply: json['hidden_supply'] as bool? ?? false,
metaInfo: json['meta_info'] as String? ?? '',
owner: json['owner'] as String? ?? '',
ticker: json['ticker'] as String? ?? '',
totalMaxSupply: json['total_max_supply'] as int? ?? 0,
totalMaxSupply: ZanoFormatter.bigIntFromDynamic(json['total_max_supply']),
isInGlobalWhitelist: isInGlobalWhitelist,
);
static const typeId = ZANO_ASSET_TYPE_ID;
static const zanoAssetsBoxName = 'zanoAssetsBox';
static const defaultOwner = '0000000000000000000000000000000000000000000000000000000000000000';

View file

@ -2,16 +2,16 @@ import 'package:cw_core/balance.dart';
import 'package:cw_zano/zano_formatter.dart';
class ZanoBalance extends Balance {
final int total;
final int unlocked;
final BigInt total;
final BigInt unlocked;
final int decimalPoint;
ZanoBalance({required this.total, required this.unlocked, this.decimalPoint = ZanoFormatter.defaultDecimalPoint}) : super(unlocked, total - unlocked);
ZanoBalance({required this.total, required this.unlocked, this.decimalPoint = ZanoFormatter.defaultDecimalPoint}) : super(unlocked.isValidInt ? unlocked.toInt() : 0, (total - unlocked).isValidInt ? (total - unlocked).toInt() : 0);
ZanoBalance.empty({this.decimalPoint = ZanoFormatter.defaultDecimalPoint}): total = 0, unlocked = 0, super(0, 0);
ZanoBalance.empty({this.decimalPoint = ZanoFormatter.defaultDecimalPoint}): total = BigInt.zero, unlocked = BigInt.zero, super(0, 0);
@override
String get formattedAdditionalBalance => ZanoFormatter.intAmountToString(total - unlocked, decimalPoint);
String get formattedAdditionalBalance => ZanoFormatter.bigIntAmountToString(total - unlocked, decimalPoint);
@override
String get formattedAvailableBalance => ZanoFormatter.intAmountToString(unlocked, decimalPoint);
String get formattedAvailableBalance => ZanoFormatter.bigIntAmountToString(unlocked, decimalPoint);
}

View file

@ -11,26 +11,27 @@ class ZanoTransactionInfo extends TransactionInfo {
required this.direction,
required this.date,
required this.isPending,
required this.amount,
required this.zanoAmount,
required this.fee,
required this.assetId,
required this.confirmations,
required this.tokenSymbol,
required this.decimalPoint,
});
}) : amount = zanoAmount.isValidInt ? zanoAmount.toInt() : 0;
ZanoTransactionInfo.fromTransfer(Transfer transfer,
{required int confirmations,
required bool isIncome,
required String assetId,
required int amount,
required BigInt amount,
this.tokenSymbol = 'ZANO',
this.decimalPoint = ZanoFormatter.defaultDecimalPoint})
: id = transfer.txHash,
height = transfer.height,
direction = isIncome ? TransactionDirection.incoming : TransactionDirection.outgoing,
date = DateTime.fromMillisecondsSinceEpoch(transfer.timestamp * 1000),
amount = amount,
zanoAmount = amount,
amount = amount.isValidInt ? amount.toInt() : 0,
fee = transfer.fee,
assetId = assetId,
confirmations = confirmations,
@ -46,6 +47,7 @@ class ZanoTransactionInfo extends TransactionInfo {
final TransactionDirection direction;
final DateTime date;
final bool isPending;
final BigInt zanoAmount;
final int amount;
final int fee;
final int confirmations;
@ -57,7 +59,7 @@ class ZanoTransactionInfo extends TransactionInfo {
String? key;
@override
String amountFormatted() => '${formatAmount(ZanoFormatter.intAmountToString(amount, decimalPoint))} $tokenSymbol';
String amountFormatted() => '${formatAmount(ZanoFormatter.bigIntAmountToString(zanoAmount, decimalPoint))} $tokenSymbol';
@override
String fiatAmount() => _fiatAmount ?? '';

View file

@ -1,21 +1,26 @@
import 'dart:math';
import 'package:cw_zano/zano_wallet_api.dart';
import 'package:decimal/decimal.dart';
import 'package:decimal/intl.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
class ZanoFormatter {
static const defaultDecimalPoint = 12;
static final numberFormat = NumberFormat()
..maximumFractionDigits = defaultDecimalPoint
//static final numberFormat = NumberFormat()
// ..maximumFractionDigits = defaultDecimalPoint
// ..minimumFractionDigits = 1;
static Decimal _bigIntDivision({required BigInt amount, required BigInt divider}) {
return (Decimal.fromBigInt(amount) / Decimal.fromBigInt(divider)).toDecimal();
}
static String intAmountToString(int amount, [int decimalPoint = defaultDecimalPoint]) {
final numberFormat = NumberFormat()..maximumFractionDigits = decimalPoint
..minimumFractionDigits = 1;
static Decimal _bigIntDivision({required BigInt amount, required BigInt divider}) =>
(Decimal.fromBigInt(amount) / Decimal.fromBigInt(divider)).toDecimal();
static String intAmountToString(int amount, [int decimalPoint = defaultDecimalPoint]) => numberFormat
.format(
return numberFormat.format(
DecimalIntl(
_bigIntDivision(
amount: BigInt.from(amount),
@ -24,8 +29,12 @@ class ZanoFormatter {
),
)
.replaceAll(',', '');
static String bigIntAmountToString(BigInt amount, [int decimalPoint = defaultDecimalPoint]) => numberFormat
.format(
}
static String bigIntAmountToString(BigInt amount, [int decimalPoint = defaultDecimalPoint]) {
final numberFormat = NumberFormat()..maximumFractionDigits = decimalPoint
..minimumFractionDigits = 1;
return numberFormat.format(
DecimalIntl(
_bigIntDivision(
amount: amount,
@ -34,12 +43,32 @@ class ZanoFormatter {
),
)
.replaceAll(',', '');
}
static double intAmountToDouble(int amount, [int decimalPoint = defaultDecimalPoint]) => _bigIntDivision(
amount: BigInt.from(amount),
divider: BigInt.from(pow(10, decimalPoint)),
).toDouble();
static int parseAmount(String amount, [int decimalPoint = defaultDecimalPoint]) =>
(Decimal.parse(amount) * Decimal.fromBigInt(BigInt.from(10).pow(decimalPoint))).toBigInt().toInt();
static int parseAmount(String amount, [int decimalPoint = defaultDecimalPoint]) {
final resultBigInt = (Decimal.parse(amount) * Decimal.fromBigInt(BigInt.from(10).pow(decimalPoint))).toBigInt();
if (!resultBigInt.isValidInt) {
Fluttertoast.showToast(msg: 'Cannot transfer $amount. Maximum is ${intAmountToString(resultBigInt.toInt(), decimalPoint)}.');
}
return resultBigInt.toInt();
}
static BigInt bigIntFromDynamic(dynamic d) {
if (d is int) {
return BigInt.from(d);
} else if (d is BigInt) {
return d;
} else if (d == null) {
return BigInt.zero;
} else {
ZanoWalletApi.error('cannot cast value of type ${d.runtimeType} to BigInt');
throw 'cannot cast value of type ${d.runtimeType} to BigInt';
//return BigInt.zero;
}
}
}

View file

@ -14,6 +14,7 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_zano/api/model/create_wallet_result.dart';
import 'package:cw_zano/api/model/destination.dart';
import 'package:cw_zano/api/model/get_recent_txs_and_info_result.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/transfer.dart';
import 'package:cw_zano/model/pending_zano_transaction.dart';
@ -29,6 +30,8 @@ import 'package:cw_zano/zano_wallet_addresses.dart';
import 'package:cw_zano/zano_wallet_api.dart';
import 'package:cw_zano/zano_wallet_exceptions.dart';
import 'package:cw_zano/zano_wallet_service.dart';
import 'package:cw_zano/api/model/balance.dart';
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';
@ -39,6 +42,7 @@ class ZanoWallet = ZanoWalletBase with _$ZanoWallet;
abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHistory, ZanoTransactionInfo> with Store, ZanoWalletApi {
static const int _autoSaveIntervalSeconds = 30;
static const int _pollIntervalMilliseconds = 2000;
static const int _maxLoadAssetsRetries = 5;
@override
ZanoWalletAddresses walletAddresses;
@ -101,7 +105,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
await wallet.connectToNode(node: Node());
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
final createWalletResult = await wallet.createWallet(path, credentials.password!);
await _parseCreateWalletResult(createWalletResult, wallet);
await wallet.parseCreateWalletResult(createWalletResult);
await wallet.init(createWalletResult.wi.address);
return wallet;
}
@ -111,7 +115,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
await wallet.connectToNode(node: Node());
final path = await pathForWallet(name: credentials.name, type: credentials.walletInfo!.type);
final createWalletResult = await wallet.restoreWalletFromSeed(path, credentials.password!, credentials.mnemonic);
await _parseCreateWalletResult(createWalletResult, wallet);
await wallet.parseCreateWalletResult(createWalletResult);
await wallet.init(createWalletResult.wi.address);
return wallet;
}
@ -121,19 +125,20 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
final wallet = ZanoWallet(walletInfo);
await wallet.connectToNode(node: Node());
final createWalletResult = await wallet.loadWallet(path, password);
await _parseCreateWalletResult(createWalletResult, wallet);
await wallet.parseCreateWalletResult(createWalletResult);
await wallet.init(createWalletResult.wi.address);
return wallet;
}
static Future<void> _parseCreateWalletResult(CreateWalletResult result, ZanoWallet wallet) async {
wallet.hWallet = result.walletId;
wallet.seed = result.seed;
Future<void> parseCreateWalletResult(CreateWalletResult result) async {
hWallet = result.walletId;
seed = result.seed;
ZanoWalletApi.info('setting hWallet = ${result.walletId}');
wallet.walletAddresses.address = result.wi.address;
walletAddresses.address = result.wi.address;
await loadAssets(result.wi.balances, maxRetries: _maxLoadAssetsRetries);
for (final item in result.wi.balances) {
if (item.assetInfo.assetId == zanoAssetId) {
wallet.balance[CryptoCurrency.zano] = ZanoBalance(
balance[CryptoCurrency.zano] = ZanoBalance(
total: item.total,
unlocked: item.unlocked,
);
@ -141,9 +146,9 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
}
if (result.recentHistory.history != null) {
final transfers = result.recentHistory.history!;
final transactions = Transfer.makeMap(transfers, wallet.zanoAssets, wallet.currentDaemonHeight);
wallet.transactionHistory.addMany(transactions);
await wallet.transactionHistory.save();
final transactions = Transfer.makeMap(transfers, zanoAssets, currentDaemonHeight);
transactionHistory.addMany(transactions);
await transactionHistory.save();
}
}
@ -167,8 +172,8 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
final isZano = credentials.currency == CryptoCurrency.zano;
final outputs = credentials.outputs;
final hasMultiDestination = outputs.length > 1;
final unlockedBalanceZano = BigInt.from(balance[CryptoCurrency.zano]?.unlocked ?? 0);
final unlockedBalanceCurrency = BigInt.from(balance[credentials.currency]?.unlocked ?? 0);
final unlockedBalanceZano = balance[CryptoCurrency.zano]?.unlocked ?? BigInt.zero;
final unlockedBalanceCurrency = balance[credentials.currency]?.unlocked ?? BigInt.zero;
final fee = BigInt.from(calculateEstimatedFee(credentials.priority));
late BigInt totalAmount;
void checkForEnoughBalances() {
@ -184,7 +189,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
}
if (totalAmount > unlockedBalanceCurrency) {
throw ZanoTransactionCreationException(
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(totalAmount)} ${credentials.currency.title}, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceZano)} ${credentials.currency.title}).");
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(totalAmount, credentials.currency.decimals)} ${credentials.currency.title}, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceCurrency, credentials.currency.decimals)} ${credentials.currency.title}).");
}
}
}
@ -241,18 +246,14 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
try {
final transfers = <Transfer>[];
late GetRecentTxsAndInfoResult result;
bool first = true;
do {
result = await getRecentTxsAndInfo(offset: _lastTxIndex, count: _txChunkSize);
// TODO: that's for debug purposes
if (first && result.transfers.isEmpty) break;
first = false;
_lastTxIndex += result.transfers.length;
transfers.addAll(result.transfers);
} while (result.lastItemIndex + 1 < result.totalTransfers);
return Transfer.makeMap(transfers, zanoAssets, currentDaemonHeight);
} catch (e) {
print(e);
ZanoWalletApi.error(e.toString());
return {};
}
}
@ -297,7 +298,33 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
await store();
await walletAddresses.updateAddressesInBox();
} catch (e) {
print('Error while saving Zano wallet file ${e.toString()}');
ZanoWalletApi.error('Error while saving Zano wallet file ${e.toString()}');
}
}
Future<void> loadAssets(List<Balance> balances, {int maxRetries = 1}) async {
List<ZanoAsset> assets = [];
int retryCount = 0;
while (retryCount < maxRetries) {
try {
assets = await getAssetsWhitelist();
break;
} on ZanoWalletBusyException {
if (retryCount < maxRetries - 1) {
retryCount++;
await Future.delayed(Duration(seconds: 1));
} else {
ZanoWalletApi.error('failed to load assets after $retryCount retries');
break;
}
}
}
zanoAssets = {};
for (final asset in assets) {
final newAsset = ZanoAsset.copyWith(asset,
icon: _getIconPath(asset.title), enabled: balances.any((element) => element.assetId == asset.assetId));
zanoAssets.putIfAbsent(asset.assetId, () => newAsset);
}
}
@ -308,7 +335,13 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
_lastKnownBlockHeight = 0;
_initialSyncHeight = 0;
_updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: _pollIntervalMilliseconds), (_) async {
final walletStatus = await getWalletStatus();
GetWalletStatusResult walletStatus;
// ignoring get wallet status exception (in case of wrong wallet id)
try {
walletStatus = await getWalletStatus();
} on ZanoWalletException {
return;
}
currentDaemonHeight = walletStatus.currentDaemonHeight;
_updateSyncProgress(walletStatus);
@ -322,14 +355,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
publicSpendKey: walletInfo.wiExtended.spendPublicKey,
publicViewKey: walletInfo.wiExtended.viewPublicKey,
);
final assets = await getAssetsWhitelist();
zanoAssets = {};
for (final asset in assets) {
final newAsset = ZanoAsset.copyWith(asset,
icon: _getIconPath(asset.title), enabled: walletInfo.wi.balances.any((element) => element.assetId == asset.assetId));
zanoAssets.putIfAbsent(asset.assetId, () => newAsset);
}
loadAssets(walletInfo.wi.balances);
// matching balances and whitelists
// 1. show only balances available in whitelists
// 2. set whitelists available in balances as 'enabled' ('disabled' by default)
@ -358,8 +384,8 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
});
} catch (e) {
syncStatus = FailedSyncStatus();
print(e);
rethrow;
ZanoWalletApi.error(e.toString());
//rethrow; // TODO: we don't need to propagate exception here
}
}
@ -377,7 +403,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
await transactionHistory.save();
_isTransactionUpdating = false;
} catch (e) {
print(e);
ZanoWalletApi.error(e.toString());
_isTransactionUpdating = false;
}
}
@ -407,12 +433,12 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
if (asset.enabled) {
final assetDescriptor = await addAssetsWhitelist(asset.assetId);
if (assetDescriptor == null) {
print('Error adding zano asset');
ZanoWalletApi.error('Error adding zano asset');
}
} else {
final result = await removeAssetsWhitelist(asset.assetId);
if (result == false) {
print('Error removing zano asset');
ZanoWalletApi.error('Error removing zano asset');
}
}
}
@ -441,7 +467,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
syncStatus = SyncingSyncStatus(blocksLeft, ptc);
}
} catch (e) {
print(e.toString());
ZanoWalletApi.error(e.toString());
}
}

View file

@ -1,5 +1,6 @@
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_zano/zano_wallet_api.dart';
import 'package:mobx/mobx.dart';
part 'zano_wallet_addresses.g.dart';
@ -33,7 +34,7 @@ abstract class ZanoWalletAddressesBase extends WalletAddresses with Store {
addressesMap[address] = '';
await saveAddressesInBox();
} catch (e) {
print(e.toString());
ZanoWalletApi.error(e.toString());
}
}
}

View file

@ -1,4 +1,4 @@
import 'dart:convert';
import 'dart:convert' as convert;
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_zano/api/api_calls.dart';
@ -20,14 +20,16 @@ import 'package:cw_zano/api/model/transfer_result.dart';
import 'package:cw_zano/model/zano_asset.dart';
import 'package:cw_zano/zano_wallet_exceptions.dart';
import 'package:flutter/foundation.dart';
import 'package:json_bigint/json_bigint.dart';
mixin ZanoWalletApi {
static const _defaultNodeUri = '195.201.107.230:33336';
static const _statusDelivered = 'delivered';
static const _maxInvokeAttempts = 10;
static const _maxReopenAttempts = 5;
static const _logInfo = true;
static const _logError = true;
static const _logJson = true;
static const _logJson = false;
static const int _zanoMixinValue = 10;
int _hWallet = 0;
@ -40,6 +42,9 @@ mixin ZanoWalletApi {
int getCurrentTxFee(TransactionPriority priority) => ApiCalls.getCurrentTxFee(priority: priority.raw);
String getOpenedWallets() => ApiCalls.getOpenedWallets();
String getConnectivityStatus() => ApiCalls.getConnectivityStatus();
void setPassword(String password) => ApiCalls.setPassword(hWallet: hWallet, password: password);
void closeWallet([int? walletToClose]) {
@ -65,7 +70,6 @@ mixin ZanoWalletApi {
final json = ApiCalls.getWalletInfo(hWallet);
final result = GetWalletInfoResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
_json('get_wallet_info', json);
//await _writeLog('get_wallet_info', 'get_wallet_info result $json');
info('get_wallet_info got ${result.wi.balances.length} balances: ${result.wi.balances} seed: ${_shorten(result.wiExtended.seed)}');
return result;
}
@ -78,7 +82,6 @@ mixin ZanoWalletApi {
}
final status = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
_json('get_wallet_status', json);
//await _writeLog('get_wallet_status', 'get_wallet_status result $json');
if (_logInfo)
info(
'get_wallet_status connected: ${status.isDaemonConnected} in refresh: ${status.isInLongRefresh} progress: ${status.progress} wallet state: ${status.walletState}');
@ -86,13 +89,13 @@ mixin ZanoWalletApi {
}
Future<String> invokeMethod(String methodName, Object params) async {
//await _writeLog(methodName, 'invoke method $methodName params: ${jsonEncode(params)} hWallet: $hWallet');
var invokeResult =
ApiCalls.asyncCall(methodName: 'invoke', hWallet: hWallet, params: '{"method": "$methodName","params": ${jsonEncode(params)}}');
Map<String, dynamic> map;
try {
map = jsonDecode(invokeResult) as Map<String, dynamic>;
} catch (e) {
if (invokeResult.contains(Consts.errorWalletWrongId)) throw ZanoWalletException('Wrong wallet id');
error('exception in parsing json in invokeMethod: $invokeResult');
rethrow;
}
@ -105,16 +108,15 @@ mixin ZanoWalletApi {
try {
map = jsonDecode(result) as Map<String, dynamic>;
} catch (e) {
if (result.contains(Consts.errorWalletWrongId)) throw ZanoWalletException('Wrong wallet id');
error('exception in parsing json in invokeMethod: $result');
rethrow;
}
if (map['status'] != null && map['status'] == _statusDelivered && map['result'] != null) {
//await _writeLog(methodName, 'invoke method $methodName result $result');
return result;
}
} while (++attempts < _maxInvokeAttempts);
}
//await _writeLog(methodName, 'invoke method $methodName result: $invokeResult');
return invokeResult;
}
@ -139,7 +141,8 @@ mixin ZanoWalletApi {
return [...globalWhitelist, ...localWhitelist, ...ownAssets];
} catch (e) {
error('assets_whitelist_get $e');
return [];
//return [];
rethrow;
}
}
@ -190,7 +193,7 @@ mixin ZanoWalletApi {
final result = await _proxyToDaemon('/json_rpc', '{"method": "$methodName","params": ${jsonEncode(params)}}');
_json('$methodName $assetId', result?.body ?? '');
if (result == null) {
debugPrint('get_asset_info empty result');
error('get_asset_info empty result');
return null;
}
final map = jsonDecode(result.body) as Map<String, dynamic>?;
@ -253,10 +256,8 @@ mixin ZanoWalletApi {
Future<CreateWalletResult> createWallet(String path, String password) async {
info('create_wallet path $path password ${_shorten(password)}');
//await _writeLog('create_wallet', 'create_wallet path $path password ${_shorten(password)}');
final json = ApiCalls.createWallet(path: path, password: password);
_json('create_wallet', json);
//await _writeLog('create_wallet', 'create_wallet result $json');
final map = jsonDecode(json) as Map<String, dynamic>?;
if (map?['error'] != null) {
final code = map!['error']!['code'] ?? '';
@ -273,10 +274,8 @@ mixin ZanoWalletApi {
Future<CreateWalletResult> restoreWalletFromSeed(String path, String password, String seed) async {
info('restore_wallet path $path password ${_shorten(password)} seed ${_shorten(seed)}');
//await _writeLog('restore_wallet', 'restore_wallet path $path password ${_shorten(password)} seed ${_shorten(seed)}');
final json = ApiCalls.restoreWalletFromSeed(path: path, password: password, seed: seed);
_json('restore_wallet', json);
//await _writeLog('restore_wallet', 'restore_wallet result $json');
final map = jsonDecode(json) as Map<String, dynamic>?;
if (map?['error'] != null) {
final code = map!['error']!['code'] ?? '';
@ -298,15 +297,19 @@ mixin ZanoWalletApi {
Future<CreateWalletResult> loadWallet(String path, String password, [int attempt = 0]) async {
info('load_wallet path $path password ${_shorten(password)}');
//await _writeLog('load_wallet', 'load_wallet path $path password ${_shorten(password)}');
final json = ApiCalls.loadWallet(path: path, password: password);
final String json;
try {
json = ApiCalls.loadWallet(path: path, password: password);
} catch (e) {
error('error in loadingWallet $e');
rethrow;
}
_json('load_wallet', json);
//await _writeLog('load_wallet', 'load_wallet result $json');
final map = jsonDecode(json) as Map<String, dynamic>?;
if (map?['error'] != null) {
final code = map?['error']!['code'] ?? '';
final message = map?['error']!['message'] ?? '';
if (code == Consts.errorAlreadyExists && attempt <= 5) {
if (code == Consts.errorAlreadyExists && attempt <= _maxReopenAttempts) {
// already connected to this wallet. closing and trying to reopen
info('already connected. closing and reopen wallet (attempt $attempt)');
closeWallet(attempt);
@ -347,11 +350,11 @@ mixin ZanoWalletApi {
final errorCode = resultMap['error']['code'];
final code = errorCode is int ? errorCode.toString() : errorCode as String? ?? '';
final message = resultMap['error']['message'] as String? ?? '';
debugPrint('transfer error $code $message');
error('transfer error $code $message');
throw TransferException('Transfer error, $message ($code)');
}
}
debugPrint('transfer error empty result');
error('transfer error empty result');
throw TransferException('Transfer error, empty result');
}
@ -366,6 +369,9 @@ mixin ZanoWalletApi {
if (result['error'] != null) {
final code = result['error']!['code'] ?? '';
final message = result['error']!['message'] ?? '';
if (code == -1 && message == Consts.errorBusy) {
throw ZanoWalletBusyException();
}
throw ZanoWalletException('Error, $message ($code)');
}
}
@ -382,5 +388,18 @@ mixin ZanoWalletApi {
static void info(String s) => _logInfo ? debugPrint('[info] $s') : null;
static void error(String s) => _logError ? debugPrint('[error] $s') : null;
static void _json(String methodName, String json) => _logJson ? debugPrint('$methodName $json') : null;
static void printWrapped(String text) => RegExp('.{1,800}').allMatches(text).map((m) => m.group(0)).forEach(print);
static void _json(String methodName, String json) => _logJson ? printWrapped('$methodName $json') : null;
Map<String, dynamic> jsonDecode(String json) {
try {
return decodeJson(json.replaceAll("\\/", "/")) as Map<String, dynamic>;
} catch (e) {
return convert.jsonDecode(json) as Map<String, dynamic>;
}
}
String jsonEncode(Object? object) {
return convert.jsonEncode(object);
}
}

View file

@ -13,3 +13,7 @@ class RestoreFromKeysException extends ZanoWalletException {
class TransferException extends ZanoWalletException {
TransferException(String message): super(message);
}
class ZanoWalletBusyException extends ZanoWalletException {
ZanoWalletBusyException(): super('');
}

View file

@ -9,6 +9,7 @@ import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_zano/api/api_calls.dart';
import 'package:cw_zano/zano_wallet.dart';
import 'package:cw_zano/zano_wallet_api.dart';
import 'package:hive/hive.dart';
class ZanoNewWalletCredentials extends WalletCredentials {
@ -54,7 +55,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials,
@override
Future<ZanoWallet> create(WalletCredentials credentials, {bool? isTestnet}) async {
print('zanowallet service create isTestnet $isTestnet');
ZanoWalletApi.info('zanowallet service create isTestnet $isTestnet');
return await ZanoWalletBase.create(credentials: credentials);
}

View file

@ -250,6 +250,19 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
fluttertoast:
dependency: "direct main"
description:
name: fluttertoast
sha256: "4215b0085ebf737120ab6b06fefeadfae709c74f880a351a73d6d007f74e7631"
url: "https://pub.dev"
source: hosted
version: "8.1.4"
frontend_server_client:
dependency: transitive
description:
@ -346,6 +359,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.8.0"
json_bigint:
dependency: "direct main"
description:
name: json_bigint
sha256: "77f5cc47ec936b37ff5016394b0ed136fe2231e35dcbaed32fe749c221264cac"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
leak_tracker:
dependency: transitive
description:
@ -490,6 +511,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.5"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.dev"
source: hosted
version: "5.4.0"
platform:
dependency: transitive
description:
@ -737,4 +766,4 @@ packages:
version: "3.1.1"
sdks:
dart: ">=3.2.0-0 <4.0.0"
flutter: ">=3.0.0"
flutter: ">=3.7.0"

View file

@ -21,6 +21,8 @@ dependencies:
decimal: ^2.3.3
cw_core:
path: ../cw_core
json_bigint: ^3.0.0
fluttertoast: 8.1.4
dev_dependencies:
flutter_test:

View file

@ -76,6 +76,8 @@ class DecimalAmountValidator extends TextValidator {
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
case CryptoCurrency.btc:
return '^([0-9]+([.\,][0-9]{1,8})?|[.\,][0-9]{1,8})\$';
case CryptoCurrency.zano:
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,18})\$';
default:
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
}

View file

@ -1,331 +0,0 @@
part of 'haven.dart';
class CWHavenAccountList extends HavenAccountList {
CWHavenAccountList(this._wallet);
final Object _wallet;
@override
@computed
ObservableList<Account> get accounts {
final havenWallet = _wallet as HavenWallet;
final accounts = havenWallet.walletAddresses.accountList.accounts
.map((acc) => Account(id: acc.id, label: acc.label))
.toList();
return ObservableList<Account>.of(accounts);
}
@override
void update(Object wallet) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.accountList.update();
}
@override
void refresh(Object wallet) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.accountList.refresh();
}
@override
List<Account> getAll(Object wallet) {
final havenWallet = wallet as HavenWallet;
return havenWallet.walletAddresses.accountList
.getAll()
.map((acc) => Account(id: acc.id, label: acc.label))
.toList();
}
@override
Future<void> addAccount(Object wallet, {required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.accountList.addAccount(label: label);
}
@override
Future<void> setLabelAccount(Object wallet,
{required int accountIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.accountList
.setLabelAccount(accountIndex: accountIndex, label: label);
}
}
class CWHavenSubaddressList extends MoneroSubaddressList {
CWHavenSubaddressList(this._wallet);
final Object _wallet;
@override
@computed
ObservableList<Subaddress> get subaddresses {
final havenWallet = _wallet as HavenWallet;
final subAddresses = havenWallet.walletAddresses.subaddressList.subaddresses
.map((sub) => Subaddress(id: sub.id, address: sub.address, label: sub.label))
.toList();
return ObservableList<Subaddress>.of(subAddresses);
}
@override
void update(Object wallet, {required int accountIndex}) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
}
@override
void refresh(Object wallet, {required int accountIndex}) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex);
}
@override
List<Subaddress> getAll(Object wallet) {
final havenWallet = wallet as HavenWallet;
return havenWallet.walletAddresses.subaddressList
.getAll()
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
.toList();
}
@override
Future<void> addSubaddress(Object wallet,
{required int accountIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.subaddressList
.addSubaddress(accountIndex: accountIndex, label: label);
}
@override
Future<void> setLabelSubaddress(Object wallet,
{required int accountIndex, required int addressIndex, required String label}) async {
final havenWallet = wallet as HavenWallet;
await havenWallet.walletAddresses.subaddressList
.setLabelSubaddress(accountIndex: accountIndex, addressIndex: addressIndex, label: label);
}
}
class CWHavenWalletDetails extends HavenWalletDetails {
CWHavenWalletDetails(this._wallet);
final Object _wallet;
@computed
@override
Account get account {
final havenWallet = _wallet as HavenWallet;
final acc = havenWallet.walletAddresses.account as monero_account.Account;
return Account(id: acc.id, label: acc.label);
}
@computed
@override
HavenBalance get balance {
final havenWallet = _wallet as HavenWallet;
final balance = havenWallet.balance;
throw Exception('Unimplemented');
//return HavenBalance(
// fullBalance: balance.fullBalance,
// unlockedBalance: balance.unlockedBalance);
}
}
class CWHaven extends Haven {
@override
HavenAccountList getAccountList(Object wallet) {
return CWHavenAccountList(wallet);
}
@override
MoneroSubaddressList getSubaddressList(Object wallet) {
return CWHavenSubaddressList(wallet);
}
@override
TransactionHistoryBase getTransactionHistory(Object wallet) {
final havenWallet = wallet as HavenWallet;
return havenWallet.transactionHistory;
}
@override
HavenWalletDetails getMoneroWalletDetails(Object wallet) {
return CWHavenWalletDetails(wallet);
}
@override
int getHeightByDate({required DateTime date}) => getHavenHeightByDate(date: date);
@override
Future<int> getCurrentHeight() => getHavenCurrentHeight();
@override
TransactionPriority getDefaultTransactionPriority() {
return MoneroTransactionPriority.automatic;
}
@override
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
return MoneroTransactionPriority.deserialize(raw: raw);
}
@override
List<TransactionPriority> getTransactionPriorities() {
return MoneroTransactionPriority.all;
}
@override
List<String> getMoneroWordList(String language) {
switch (language.toLowerCase()) {
case 'english':
return EnglishMnemonics.words;
case 'chinese (simplified)':
return ChineseSimplifiedMnemonics.words;
case 'dutch':
return DutchMnemonics.words;
case 'german':
return GermanMnemonics.words;
case 'japanese':
return JapaneseMnemonics.words;
case 'portuguese':
return PortugueseMnemonics.words;
case 'russian':
return RussianMnemonics.words;
case 'spanish':
return SpanishMnemonics.words;
case 'french':
return FrenchMnemonics.words;
case 'italian':
return ItalianMnemonics.words;
default:
return EnglishMnemonics.words;
}
}
@override
WalletCredentials createHavenRestoreWalletFromKeysCredentials(
{required String name,
required String spendKey,
required String viewKey,
required String address,
required String password,
required String language,
required int height}) {
return HavenRestoreWalletFromKeysCredentials(
name: name,
spendKey: spendKey,
viewKey: viewKey,
address: address,
password: password,
language: language,
height: height);
}
@override
WalletCredentials createHavenRestoreWalletFromSeedCredentials(
{required String name,
required String password,
required int height,
required String mnemonic}) {
return HavenRestoreWalletFromSeedCredentials(
name: name, password: password, height: height, mnemonic: mnemonic);
}
@override
WalletCredentials createHavenNewWalletCredentials(
{required String name, required String language, String? password}) {
return HavenNewWalletCredentials(name: name, password: password, language: language);
}
@override
Map<String, String> getKeys(Object wallet) {
final havenWallet = wallet as HavenWallet;
final keys = havenWallet.keys;
return <String, String>{
'privateSpendKey': keys.privateSpendKey,
'privateViewKey': keys.privateViewKey,
'publicSpendKey': keys.publicSpendKey,
'publicViewKey': keys.publicViewKey
};
}
@override
Object createHavenTransactionCreationCredentials(
{required List<Output> outputs,
required TransactionPriority priority,
required String assetType}) {
return HavenTransactionCreationCredentials(
outputs: outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as MoneroTransactionPriority,
assetType: assetType);
}
@override
String formatterMoneroAmountToString({required int amount}) {
return moneroAmountToString(amount: amount);
}
@override
double formatterMoneroAmountToDouble({required int amount}) {
return moneroAmountToDouble(amount: amount);
}
@override
int formatterMoneroParseAmount({required String amount}) {
return moneroParseAmount(amount: amount);
}
@override
Account getCurrentAccount(Object wallet) {
final havenWallet = wallet as HavenWallet;
final acc = havenWallet.walletAddresses.account as monero_account.Account;
return Account(id: acc.id, label: acc.label);
}
@override
void setCurrentAccount(Object wallet, int id, String label) {
final havenWallet = wallet as HavenWallet;
havenWallet.walletAddresses.account = monero_account.Account(id: id, label: label);
}
@override
void onStartup() {
monero_wallet_api.onStartup();
}
@override
int getTransactionInfoAccountId(TransactionInfo tx) {
final havenTransactionInfo = tx as HavenTransactionInfo;
return havenTransactionInfo.accountIndex;
}
@override
WalletService createHavenWalletService(Box<WalletInfo> walletInfoSource) {
return HavenWalletService(walletInfoSource);
}
@override
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
final havenWallet = wallet as HavenWallet;
return havenWallet.getTransactionAddress(accountIndex, addressIndex);
}
@override
CryptoCurrency assetOfTransaction(TransactionInfo tx) {
final transaction = tx as HavenTransactionInfo;
final asset = CryptoCurrency.fromString(transaction.assetType);
return asset;
}
@override
List<AssetRate> getAssetRate() =>
getRate().map((rate) => AssetRate(rate.getAssetType(), rate.getRate())).toList();
}

View file

@ -88,9 +88,7 @@ abstract class HomeSettingsViewModelBase with Store {
}
if (_balanceViewModel.wallet.type == WalletType.zano) {
// TODO: assuming that token is Erc20Token
token as Erc20Token;
await zano!.addZanoAssetById(_balanceViewModel.wallet, token.contractAddress);
await zano!.addZanoAssetById(_balanceViewModel.wallet, contractAddress);
}
_updateTokensList();

View file

@ -177,7 +177,7 @@ class TransactionListItem extends ActionListItem with Keyable {
final asset = zano!.assetOfTransaction(balanceViewModel.wallet, transaction);
final price = balanceViewModel.fiatConvertationStore.prices[asset];
amount = calculateFiatAmountRaw(
cryptoAmount: zano!.formatterIntAmountToDouble(amount: transaction.amount, currency: asset),
cryptoAmount: zano!.formatterIntAmountToDouble(amount: transaction.amount, currency: asset, forFee: false),
price: price);
break;
default:

View file

@ -183,7 +183,7 @@ abstract class OutputBase with Store {
}
if (_wallet.type == WalletType.zano) {
return zano!.formatterIntAmountToDouble(amount: fee, currency: cryptoCurrencyHandler());
return zano!.formatterIntAmountToDouble(amount: fee, currency: cryptoCurrencyHandler(), forFee: true);
}
} catch (e) {
print(e.toString());

View file

@ -20,6 +20,7 @@ import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:collection/collection.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_zano/model/zano_transaction_info.dart';
import 'package:hive/hive.dart';
import 'package:intl/src/intl/date_format.dart';
import 'package:mobx/mobx.dart';
@ -497,9 +498,11 @@ abstract class TransactionDetailsViewModelBase with Store {
}
void _addZanoListItems(TransactionInfo tx, DateFormat dateFormat) {
tx as ZanoTransactionInfo;
final comment = tx.additionalInfo['comment'] as String?;
items.addAll([
StandartListItem(title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(title: 'Asset ID', value: tx.assetId),
StandartListItem(
title: S.current.transaction_details_date, value: dateFormat.format(tx.date)),
StandartListItem(title: S.current.transaction_details_height, value: '${tx.height}'),

View file

@ -102,7 +102,9 @@ class CWZano extends Zano {
}
@override
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency}) {
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee}) {
// fee always counted in zano with default decimal points
if (forFee) return ZanoFormatter.intAmountToDouble(amount);
if (currency is ZanoAsset) return ZanoFormatter.intAmountToDouble(amount, currency.decimalPoint);
return ZanoFormatter.intAmountToDouble(amount);
}

View file

@ -42,7 +42,7 @@ abstract class Zano {
WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic});
WalletCredentials createZanoNewWalletCredentials({required String name, String password});
Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency});
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency});
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee});
int formatterParseAmount({required String amount, required CryptoCurrency currency});
WalletService createZanoWalletService(Box<WalletInfo> walletInfoSource);
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo tx);

View file

@ -1394,7 +1394,7 @@ abstract class Zano {
WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic});
WalletCredentials createZanoNewWalletCredentials({required String name, String password});
Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency});
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency});
double formatterIntAmountToDouble({required int amount, required CryptoCurrency currency, required bool forFee});
int formatterParseAmount({required String amount, required CryptoCurrency currency});
WalletService createZanoWalletService(Box<WalletInfo> walletInfoSource);
CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo tx);