mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-03 09:29:48 +00:00
changed fields to BigInt, some fixes
This commit is contained in:
parent
e7b29c0a56
commit
02bfe643d9
26 changed files with 262 additions and 455 deletions
|
@ -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}) {
|
||||
|
|
|
@ -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';
|
||||
}
|
|
@ -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']),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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)
|
||||
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 {
|
|||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 ?? '';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,3 +13,7 @@ class RestoreFromKeysException extends ZanoWalletException {
|
|||
class TransferException extends ZanoWalletException {
|
||||
TransferException(String message): super(message);
|
||||
}
|
||||
|
||||
class ZanoWalletBusyException extends ZanoWalletException {
|
||||
ZanoWalletBusyException(): super('');
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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})\$';
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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}'),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue