diff --git a/cw_zano/lib/api/api_calls.dart b/cw_zano/lib/api/api_calls.dart index 08f8a96d1..43fac9367 100644 --- a/cw_zano/lib/api/api_calls.dart +++ b/cw_zano/lib/api/api_calls.dart @@ -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 Function(int hWallet, Pointer passwor // char* get_connectivity_status() // char* get_version() +// get_opened_wallets() typedef _stringFunction = Pointer Function(); class ApiCalls { static String _convertUTF8ToString({required Pointer pointer}) { - final str = pointer.toDartString(); - //final str = pointer.toDartStringAllowingMalformed(); + //final str = pointer.toDartString(); + final str = pointer.toDartStringAllowingMalformed(); calloc.free(pointer); return str; } @@ -181,8 +183,11 @@ class ApiCalls { static int getCurrentTxFee({required int priority}) => _getCurrentTxFeeNative(priority); static final _getConnectivityStatusNative = zanoApi.lookup>('get_connectivity_status').asFunction<_stringFunction>(); - + static String getConnectivityStatus() => _performApiCall(() => _getConnectivityStatusNative()); + + static final _getOpenedWalletsNative = zanoApi.lookup>('get_opened_wallets').asFunction<_stringFunction>(); + static String getOpenedWallets() => _performApiCall(() => _getOpenedWalletsNative()); static final _getAddressInfoNative = zanoApi.lookup>('get_address_info').asFunction<_GetAddressInfo>(); diff --git a/cw_zano/lib/api/consts.dart b/cw_zano/lib/api/consts.dart index f3c64f648..80002b880 100644 --- a/cw_zano/lib/api/consts.dart +++ b/cw_zano/lib/api/consts.dart @@ -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'; } \ No newline at end of file diff --git a/cw_zano/lib/api/model/balance.dart b/cw_zano/lib/api/model/balance.dart index 386bc5173..8ea48d2a4 100644 --- a/cw_zano/lib/api/model/balance.dart +++ b/cw_zano/lib/api/model/balance.dart @@ -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 json) => Balance( assetInfo: ZanoAsset.fromJson(json['asset_info'] as Map? ?? {}), - 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']), ); } diff --git a/cw_zano/lib/api/model/receive.dart b/cw_zano/lib/api/model/receive.dart index da0443b29..6364bf181 100644 --- a/cw_zano/lib/api/model/receive.dart +++ b/cw_zano/lib/api/model/receive.dart @@ -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 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, ); diff --git a/cw_zano/lib/api/model/subtransfer.dart b/cw_zano/lib/api/model/subtransfer.dart index 807134be3..d92f1407a 100644 --- a/cw_zano/lib/api/model/subtransfer.dart +++ b/cw_zano/lib/api/model/subtransfer.dart @@ -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 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, ); diff --git a/cw_zano/lib/api/model/transfer.dart b/cw_zano/lib/api/model/transfer.dart index 1863a5676..460afcfef 100644 --- a/cw_zano/lib/api/model/transfer.dart +++ b/cw_zano/lib/api/model/transfer.dart @@ -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 makeMap(List transfers, Map zanoAssets, int currentDaemonHeight) => - Map.fromIterable( + static Map makeMap(List transfers, Map 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) - 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, - ); + 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 ? 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 { ); }, ); + } } diff --git a/cw_zano/lib/model/zano_asset.dart b/cw_zano/lib/model/zano_asset.dart index 9cee2b4c7..58f53738e 100644 --- a/cw_zano/lib/model/zano_asset.dart +++ b/cw_zano/lib/model/zano_asset.dart @@ -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,11 +47,11 @@ 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, + }) : _enabled = enabled, super( name: fullName, title: ticker.toUpperCase(), @@ -86,17 +86,19 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin { factory ZanoAsset.fromJson(Map 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'; diff --git a/cw_zano/lib/model/zano_balance.dart b/cw_zano/lib/model/zano_balance.dart index 77baeaa63..882c0e11b 100644 --- a/cw_zano/lib/model/zano_balance.dart +++ b/cw_zano/lib/model/zano_balance.dart @@ -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); } diff --git a/cw_zano/lib/model/zano_transaction_info.dart b/cw_zano/lib/model/zano_transaction_info.dart index 4fe3a9d89..8b0d4c3cd 100644 --- a/cw_zano/lib/model/zano_transaction_info.dart +++ b/cw_zano/lib/model/zano_transaction_info.dart @@ -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 ?? ''; diff --git a/cw_zano/lib/zano_formatter.dart b/cw_zano/lib/zano_formatter.dart index 92851e388..ffc5d20f3 100644 --- a/cw_zano/lib/zano_formatter.dart +++ b/cw_zano/lib/zano_formatter.dart @@ -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 - ..minimumFractionDigits = 1; + //static final numberFormat = NumberFormat() + // ..maximumFractionDigits = defaultDecimalPoint + // ..minimumFractionDigits = 1; - static Decimal _bigIntDivision({required BigInt amount, required BigInt divider}) => - (Decimal.fromBigInt(amount) / Decimal.fromBigInt(divider)).toDecimal(); + 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]) => numberFormat - .format( + static String intAmountToString(int amount, [int decimalPoint = defaultDecimalPoint]) { + final numberFormat = NumberFormat()..maximumFractionDigits = decimalPoint + ..minimumFractionDigits = 1; + 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; + } + } } diff --git a/cw_zano/lib/zano_wallet.dart b/cw_zano/lib/zano_wallet.dart index 60ff3146c..fcc503c2a 100644 --- a/cw_zano/lib/zano_wallet.dart +++ b/cw_zano/lib/zano_wallet.dart @@ -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 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 _parseCreateWalletResult(CreateWalletResult result, ZanoWallet wallet) async { - wallet.hWallet = result.walletId; - wallet.seed = result.seed; + Future 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 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 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[]; 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 loadAssets(List balances, {int maxRetries = 1}) async { + List 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 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 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); _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); _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 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 map; try { map = jsonDecode(invokeResult) as Map; } 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; } 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?; @@ -253,10 +256,8 @@ mixin ZanoWalletApi { Future 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?; if (map?['error'] != null) { final code = map!['error']!['code'] ?? ''; @@ -273,10 +274,8 @@ mixin ZanoWalletApi { Future 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?; if (map?['error'] != null) { final code = map!['error']!['code'] ?? ''; @@ -298,15 +297,19 @@ mixin ZanoWalletApi { Future 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?; 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 jsonDecode(String json) { + try { + return decodeJson(json.replaceAll("\\/", "/")) as Map; + } catch (e) { + return convert.jsonDecode(json) as Map; + } + } + + String jsonEncode(Object? object) { + return convert.jsonEncode(object); + } } diff --git a/cw_zano/lib/zano_wallet_exceptions.dart b/cw_zano/lib/zano_wallet_exceptions.dart index 4e82cb2aa..57767ef90 100644 --- a/cw_zano/lib/zano_wallet_exceptions.dart +++ b/cw_zano/lib/zano_wallet_exceptions.dart @@ -12,4 +12,8 @@ class RestoreFromKeysException extends ZanoWalletException { class TransferException extends ZanoWalletException { TransferException(String message): super(message); +} + +class ZanoWalletBusyException extends ZanoWalletException { + ZanoWalletBusyException(): super(''); } \ No newline at end of file diff --git a/cw_zano/lib/zano_wallet_service.dart b/cw_zano/lib/zano_wallet_service.dart index cfe97e041..3c4f60685 100644 --- a/cw_zano/lib/zano_wallet_service.dart +++ b/cw_zano/lib/zano_wallet_service.dart @@ -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 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); } diff --git a/cw_zano/pubspec.lock b/cw_zano/pubspec.lock index c30a35db6..e1c5eb2f3 100644 --- a/cw_zano/pubspec.lock +++ b/cw_zano/pubspec.lock @@ -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" diff --git a/cw_zano/pubspec.yaml b/cw_zano/pubspec.yaml index 604480d76..f8ecb3150 100644 --- a/cw_zano/pubspec.yaml +++ b/cw_zano/pubspec.yaml @@ -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: diff --git a/lib/core/amount_validator.dart b/lib/core/amount_validator.dart index 38983dfb2..f85df483f 100644 --- a/lib/core/amount_validator.dart +++ b/lib/core/amount_validator.dart @@ -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})\$'; } diff --git a/lib/haven/cw_haven.dart b/lib/haven/cw_haven.dart deleted file mode 100644 index 57c4e49c3..000000000 --- a/lib/haven/cw_haven.dart +++ /dev/null @@ -1,331 +0,0 @@ -part of 'haven.dart'; - -class CWHavenAccountList extends HavenAccountList { - CWHavenAccountList(this._wallet); - - final Object _wallet; - - @override - @computed - ObservableList 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.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 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 addAccount(Object wallet, {required String label}) async { - final havenWallet = wallet as HavenWallet; - await havenWallet.walletAddresses.accountList.addAccount(label: label); - } - - @override - Future 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 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.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 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 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 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 getCurrentHeight() => getHavenCurrentHeight(); - - @override - TransactionPriority getDefaultTransactionPriority() { - return MoneroTransactionPriority.automatic; - } - - @override - TransactionPriority deserializeMoneroTransactionPriority({required int raw}) { - return MoneroTransactionPriority.deserialize(raw: raw); - } - - @override - List getTransactionPriorities() { - return MoneroTransactionPriority.all; - } - - @override - List 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 getKeys(Object wallet) { - final havenWallet = wallet as HavenWallet; - final keys = havenWallet.keys; - return { - 'privateSpendKey': keys.privateSpendKey, - 'privateViewKey': keys.privateViewKey, - 'publicSpendKey': keys.publicSpendKey, - 'publicViewKey': keys.publicViewKey - }; - } - - @override - Object createHavenTransactionCreationCredentials( - {required List 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 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 getAssetRate() => - getRate().map((rate) => AssetRate(rate.getAssetType(), rate.getRate())).toList(); -} diff --git a/lib/view_model/dashboard/home_settings_view_model.dart b/lib/view_model/dashboard/home_settings_view_model.dart index 738a75c1c..a0f04de49 100644 --- a/lib/view_model/dashboard/home_settings_view_model.dart +++ b/lib/view_model/dashboard/home_settings_view_model.dart @@ -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(); diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index fc67a5853..d064a524b 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -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: diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index fdc3015e0..062f3712e 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -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()); diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index ae116e3c1..ac0d5aa3d 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -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}'), diff --git a/lib/zano/cw_zano.dart b/lib/zano/cw_zano.dart index 09fbb0b8f..e6207ee45 100644 --- a/lib/zano/cw_zano.dart +++ b/lib/zano/cw_zano.dart @@ -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); } diff --git a/lib/zano/zano.dart b/lib/zano/zano.dart index 0c3adf126..69999cb3c 100644 --- a/lib/zano/zano.dart +++ b/lib/zano/zano.dart @@ -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 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 walletInfoSource); CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo tx); diff --git a/tool/configure.dart b/tool/configure.dart index 611e416e0..49429a36c 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -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 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 walletInfoSource); CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo tx);