transfers in different assets

This commit is contained in:
leo 2024-03-16 10:55:03 +00:00
parent b2b6578972
commit c74a114209
20 changed files with 211 additions and 186 deletions

View file

@ -2,7 +2,6 @@ import 'package:decimal/decimal.dart';
import 'package:decimal/intl.dart'; import 'package:decimal/intl.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:rational/rational.dart';
class AmountConverter { class AmountConverter {
static const _moneroAmountLength = 12; static const _moneroAmountLength = 12;

View file

@ -1,38 +0,0 @@
// class AssetDescriptor {
// static const defaultDecimalPoint = 12;
// final String assetId;
// final int currentSupply;
// final int decimalPoint;
// final String fullName;
// final bool hiddenSupply;
// final String metaInfo;
// final String owner;
// final String ticker;
// final int totalMaxSupply;
// AssetDescriptor({
// required this.assetId,
// required this.currentSupply,
// required this.decimalPoint,
// required this.fullName,
// required this.hiddenSupply,
// required this.metaInfo,
// required this.owner,
// required this.ticker,
// required this.totalMaxSupply,
// });
// factory AssetDescriptor.fromJson(Map<String, dynamic> json) =>
// AssetDescriptor(
// assetId: json['asset_id'] as String? ?? '',
// currentSupply: json['current_supply'] as int? ?? 0,
// decimalPoint: json['decimal_point'] as int? ?? 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,
// );
// }

View file

@ -1,5 +1,5 @@
class Destination { class Destination {
final int amount; // transfered as string final BigInt amount; // transfered as string
final String address; final String address;
final String assetId; final String assetId;
@ -7,7 +7,7 @@ class Destination {
{required this.amount, required this.address, required this.assetId}); {required this.amount, required this.address, required this.assetId});
factory Destination.fromJson(Map<String, dynamic> json) => Destination( factory Destination.fromJson(Map<String, dynamic> json) => Destination(
amount: int.parse(json['amount'] as String? ?? '0'), amount: BigInt.parse(json['amount'] as String? ?? '0'),
address: json['address'] as String? ?? '', address: json['address'] as String? ?? '',
assetId: json['asset_id'] as String? ?? '', assetId: json['asset_id'] as String? ?? '',
); );

View file

@ -1,7 +1,7 @@
import 'package:cw_zano/api/model/history.dart'; import 'package:cw_zano/api/model/transfer.dart';
class RecentHistory { class RecentHistory {
final List<History>? history; final List<Transfer>? history;
final int lastItemIndex; final int lastItemIndex;
final int totalHistoryItems; final int totalHistoryItems;
@ -12,7 +12,7 @@ class RecentHistory {
factory RecentHistory.fromJson(Map<String, dynamic> json) => RecentHistory( factory RecentHistory.fromJson(Map<String, dynamic> json) => RecentHistory(
history: json['history'] == null ? null : (json['history'] as List<dynamic>) history: json['history'] == null ? null : (json['history'] as List<dynamic>)
.map((e) => History.fromJson(e as Map<String, dynamic>)) .map((e) => Transfer.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
lastItemIndex: json['last_item_index'] as int? ?? 0, lastItemIndex: json['last_item_index'] as int? ?? 0,
totalHistoryItems: json['total_history_items'] as int? ?? 0, totalHistoryItems: json['total_history_items'] as int? ?? 0,

View file

@ -1,7 +1,7 @@
import 'package:cw_zano/api/model/employed_entries.dart'; import 'package:cw_zano/api/model/employed_entries.dart';
import 'package:cw_zano/api/model/subtransfer.dart'; import 'package:cw_zano/api/model/subtransfer.dart';
class History { class Transfer {
final String comment; final String comment;
final EmployedEntries employedEntries; final EmployedEntries employedEntries;
final int fee; final int fee;
@ -21,7 +21,7 @@ class History {
final int txType; final int txType;
final int unlockTime; final int unlockTime;
History({ Transfer({
required this.comment, required this.comment,
required this.employedEntries, required this.employedEntries,
required this.fee, required this.fee,
@ -42,7 +42,7 @@ class History {
required this.unlockTime, required this.unlockTime,
}); });
factory History.fromJson(Map<String, dynamic> json) => History( factory Transfer.fromJson(Map<String, dynamic> json) => Transfer(
comment: json['comment'] as String? ?? '', comment: json['comment'] as String? ?? '',
employedEntries: EmployedEntries.fromJson( employedEntries: EmployedEntries.fromJson(
json['employed_entries'] as Map<String, dynamic>? ?? {}), json['employed_entries'] as Map<String, dynamic>? ?? {}),

View file

@ -2,7 +2,7 @@ import 'package:cw_zano/api/model/destination.dart';
class TransferParams { class TransferParams {
final List<Destination> destinations; final List<Destination> destinations;
final int fee; final BigInt fee;
final int mixin; final int mixin;
final String paymentId; final String paymentId;
final String comment; final String comment;
@ -31,7 +31,7 @@ class TransferParams {
factory TransferParams.fromJson(Map<String, dynamic> json) => TransferParams( factory TransferParams.fromJson(Map<String, dynamic> json) => TransferParams(
destinations: (json['destinations'] as List<dynamic>?)?.map((e) => Destination.fromJson(e as Map<String, dynamic>)).toList() ?? [], destinations: (json['destinations'] as List<dynamic>?)?.map((e) => Destination.fromJson(e as Map<String, dynamic>)).toList() ?? [],
fee: json['fee'] as int? ?? 0, fee: BigInt.from(json['fee'] as int? ?? 0),
mixin: json['mixin'] as int? ?? 0, mixin: json['mixin'] as int? ?? 0,
paymentId: json['payment_id'] as String? ?? '', paymentId: json['payment_id'] as String? ?? '',
comment: json['comment'] as String? ?? '', comment: json['comment'] as String? ?? '',

View file

@ -1,13 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_zano/api/exceptions/transfer_exception.dart'; import 'package:cw_zano/api/exceptions/transfer_exception.dart';
import 'package:cw_zano/api/model/destination.dart'; import 'package:cw_zano/api/model/destination.dart';
import 'package:cw_zano/api/model/history.dart';
import 'package:cw_zano/api/model/transfer_params.dart'; import 'package:cw_zano/api/model/transfer_params.dart';
import 'package:cw_zano/api/model/transfer_result.dart'; import 'package:cw_zano/api/model/transfer_result.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_zano/zano_formatter.dart';
import 'package:cw_core/amount_converter.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_zano/zano_wallet.dart'; import 'package:cw_zano/zano_wallet.dart';
class PendingZanoTransaction with PendingTransaction { class PendingZanoTransaction with PendingTransaction {
@ -16,12 +14,20 @@ class PendingZanoTransaction with PendingTransaction {
required this.destinations, required this.destinations,
required this.fee, required this.fee,
required this.comment, required this.comment,
required this.assetId,
required this.ticker,
this.decimalPoint = 12,
required this.amount,
}); });
final ZanoWalletBase zanoWallet; final ZanoWalletBase zanoWallet;
final List<Destination> destinations; final List<Destination> destinations;
final int fee; final BigInt fee;
final String comment; final String comment;
final String assetId;
final String ticker;
final int decimalPoint;
final BigInt amount;
@override @override
String get id => transferResult?.txHash ?? ''; String get id => transferResult?.txHash ?? '';
@ -30,10 +36,10 @@ class PendingZanoTransaction with PendingTransaction {
String get hex => ''; String get hex => '';
@override @override
String get amountFormatted => AmountConverter.amountIntToString(CryptoCurrency.zano, destinations.first.amount); String get amountFormatted => '${ZanoFormatter.bigIntAmountToString(amount, decimalPoint)} $ticker';
@override @override
String get feeFormatted => AmountConverter.amountIntToString(CryptoCurrency.zano, fee); String get feeFormatted => '${ZanoFormatter.bigIntAmountToString(fee)} ZANO';
TransferResult? transferResult; TransferResult? transferResult;
@ -66,4 +72,6 @@ class PendingZanoTransaction with PendingTransaction {
} }
} }
} }
} }

View file

@ -1,5 +1,6 @@
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_zano/zano_formatter.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
part 'zano_asset.g.dart'; part 'zano_asset.g.dart';
@ -39,7 +40,7 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
this.fullName = '', this.fullName = '',
this.ticker = '', this.ticker = '',
required this.assetId, required this.assetId,
this.decimalPoint = defaultDecimalPoint, this.decimalPoint = ZanoFormatter.defaultDecimalPoint,
bool enabled = true, bool enabled = true,
this.iconPath, this.iconPath,
this.tag, this.tag,
@ -83,7 +84,7 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
factory ZanoAsset.fromJson(Map<String, dynamic> json) => ZanoAsset( factory ZanoAsset.fromJson(Map<String, dynamic> json) => ZanoAsset(
assetId: json['asset_id'] as String? ?? '', assetId: json['asset_id'] as String? ?? '',
currentSupply: json['current_supply'] as int? ?? 0, currentSupply: json['current_supply'] as int? ?? 0,
decimalPoint: json['decimal_point'] as int? ?? defaultDecimalPoint, decimalPoint: json['decimal_point'] as int? ?? ZanoFormatter.defaultDecimalPoint,
fullName: json['full_name'] as String? ?? '', fullName: json['full_name'] as String? ?? '',
hiddenSupply: json['hidden_supply'] as bool? ?? false, hiddenSupply: json['hidden_supply'] as bool? ?? false,
metaInfo: json['meta_info'] as String? ?? '', metaInfo: json['meta_info'] as String? ?? '',
@ -92,11 +93,7 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
totalMaxSupply: json['total_max_supply'] as int? ?? 0, totalMaxSupply: json['total_max_supply'] as int? ?? 0,
); );
@override
String toString() => '$ticker (${assetId.substring(0, 4)}...${assetId.substring(assetId.length - 4)})';
static const typeId = ZANO_ASSET_TYPE_ID; static const typeId = ZANO_ASSET_TYPE_ID;
static const zanoAssetsBoxName = 'zanoAssetsBox'; static const zanoAssetsBoxName = 'zanoAssetsBox';
static const defaultDecimalPoint = 12;
static const defaultOwner = '0000000000000000000000000000000000000000000000000000000000000000'; static const defaultOwner = '0000000000000000000000000000000000000000000000000000000000000000';
} }

View file

@ -0,0 +1,24 @@
import 'dart:math';
import 'package:decimal/decimal.dart';
import 'package:decimal/intl.dart';
import 'package:intl/intl.dart';
class ZanoFormatter {
static const defaultDecimalPoint = 12;
//static const _moneroAmountLength = 12;
static final numberFormat = NumberFormat()
..maximumFractionDigits = defaultDecimalPoint
..minimumFractionDigits = 1;
static Decimal _intDivision({required int amount, required BigInt divider}) => (Decimal.fromInt(amount) / Decimal.fromBigInt(divider)).toDecimal();
static Decimal _bigIntDivision({required BigInt amount, required BigInt divider}) => (Decimal.fromBigInt(amount) / Decimal.fromBigInt(divider)).toDecimal();
static String intAmountToString(int amount, [int decimalPoint = defaultDecimalPoint]) => numberFormat.format(DecimalIntl(
_intDivision(amount: amount, divider: BigInt.from(pow(10, decimalPoint))),
),);
static String bigIntAmountToString(BigInt amount, [int decimalPoint = defaultDecimalPoint]) => numberFormat.format(DecimalIntl(
_bigIntDivision(amount: amount, divider: BigInt.from(pow(10, decimalPoint))),
),);
}

View file

@ -1,10 +1,12 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/output_info.dart'; import 'package:cw_core/output_info.dart';
class ZanoTransactionCredentials { class ZanoTransactionCredentials {
ZanoTransactionCredentials({required this.outputs, required this.priority, required this.assetType}); ZanoTransactionCredentials({required this.outputs, required this.priority, required this.currency});
final List<OutputInfo> outputs; final List<OutputInfo> outputs;
final MoneroTransactionPriority priority; final MoneroTransactionPriority priority;
final String assetType; final CryptoCurrency currency;
//final String assetType;
} }

View file

@ -2,35 +2,37 @@ import 'package:cw_core/format_amount.dart';
import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
import 'package:cw_zano/api/model/history.dart'; import 'package:cw_zano/api/model/transfer.dart';
class ZanoTransactionInfo extends TransactionInfo { class ZanoTransactionInfo extends TransactionInfo {
ZanoTransactionInfo( ZanoTransactionInfo({
this.id, required this.id,
this.height, required this.height,
this.direction, required this.direction,
this.date, required this.date,
this.isPending, required this.isPending,
this.amount, required this.amount,
this.accountIndex, required this.accountIndex,
this.addressIndex, required this.addressIndex,
this.fee, required this.fee,
this.confirmations); required this.assetId,
required this.confirmations,
required this.tokenSymbol,
});
ZanoTransactionInfo.fromHistory(History history) ZanoTransactionInfo.fromTransfer(Transfer transfer, this.tokenSymbol)
: id = history.txHash, : id = transfer.txHash,
height = history.height, height = transfer.height,
direction = history.subtransfers.first.isIncome ? TransactionDirection.incoming : direction = transfer.subtransfers.first.isIncome ? TransactionDirection.incoming : TransactionDirection.outgoing,
TransactionDirection.outgoing, date = DateTime.fromMillisecondsSinceEpoch(transfer.timestamp * 1000),
date = DateTime.fromMillisecondsSinceEpoch(history.timestamp * 1000), isPending = false,
isPending = false, amount = transfer.subtransfers.first.amount,
amount = history.subtransfers.first.amount, accountIndex = 0,
accountIndex = 0, addressIndex = 0,
addressIndex = 0, fee = transfer.fee,
fee = history.fee, confirmations = 1,
confirmations = 1, assetId = transfer.subtransfers.first.assetId,
assetType = 'ZANO', // TODO: FIXIT: recipientAddress = transfer.remoteAddresses.isNotEmpty ? transfer.remoteAddresses.first : '';
recipientAddress = history.remoteAddresses.isNotEmpty ? history.remoteAddresses.first : '';
final String id; final String id;
final int height; final int height;
@ -43,13 +45,13 @@ class ZanoTransactionInfo extends TransactionInfo {
final int addressIndex; final int addressIndex;
final int confirmations; final int confirmations;
late String recipientAddress; late String recipientAddress;
late String assetType; final String tokenSymbol;
late String assetId;
String? _fiatAmount; String? _fiatAmount;
String? key; String? key;
@override @override
String amountFormatted() => String amountFormatted() => '${formatAmount(moneroAmountToString(amount: amount))} $tokenSymbol';
'${formatAmount(moneroAmountToString(amount: amount))} $assetType';
@override @override
String fiatAmount() => _fiatAmount ?? ''; String fiatAmount() => _fiatAmount ?? '';
@ -58,6 +60,7 @@ class ZanoTransactionInfo extends TransactionInfo {
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
@override @override
String feeFormatted() => String feeFormatted() => '${formatAmount(moneroAmountToString(amount: fee))} $feeCurrency';
'${formatAmount(moneroAmountToString(amount: fee))} $assetType';
String get feeCurrency => 'ZANO';
} }

View file

@ -15,12 +15,13 @@ import 'package:cw_core/wallet_info.dart';
import 'package:cw_zano/api/api_calls.dart'; import 'package:cw_zano/api/api_calls.dart';
import 'package:cw_zano/api/model/destination.dart'; import 'package:cw_zano/api/model/destination.dart';
import 'package:cw_zano/api/model/get_wallet_status_result.dart'; import 'package:cw_zano/api/model/get_wallet_status_result.dart';
import 'package:cw_zano/api/model/history.dart'; import 'package:cw_zano/api/model/transfer.dart';
import 'package:cw_zano/api/model/zano_wallet_keys.dart'; import 'package:cw_zano/api/model/zano_wallet_keys.dart';
import 'package:cw_zano/exceptions/zano_transaction_creation_exception.dart'; import 'package:cw_zano/exceptions/zano_transaction_creation_exception.dart';
import 'package:cw_zano/pending_zano_transaction.dart'; import 'package:cw_zano/pending_zano_transaction.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/zano_asset.dart';
import 'package:cw_zano/zano_balance.dart'; import 'package:cw_zano/zano_balance.dart';
import 'package:cw_zano/zano_formatter.dart';
import 'package:cw_zano/zano_transaction_credentials.dart'; import 'package:cw_zano/zano_transaction_credentials.dart';
import 'package:cw_zano/zano_transaction_history.dart'; import 'package:cw_zano/zano_transaction_history.dart';
import 'package:cw_zano/zano_transaction_info.dart'; import 'package:cw_zano/zano_transaction_info.dart';
@ -41,7 +42,7 @@ class ZanoWallet = ZanoWalletBase with _$ZanoWallet;
abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHistory, ZanoTransactionInfo> with Store, ZanoWalletApi { abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHistory, ZanoTransactionInfo> with Store, ZanoWalletApi {
static const int _autoSaveInterval = 30; static const int _autoSaveInterval = 30;
List<History> history = []; List<Transfer> transfers = [];
//String defaultAsssetId = ''; //String defaultAsssetId = '';
@override @override
ZanoWalletAddresses walletAddresses; ZanoWalletAddresses walletAddresses;
@ -60,6 +61,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
@override @override
ZanoWalletKeys keys = ZanoWalletKeys(privateSpendKey: '', privateViewKey: '', publicSpendKey: '', publicViewKey: ''); ZanoWalletKeys keys = ZanoWalletKeys(privateSpendKey: '', privateViewKey: '', publicSpendKey: '', publicViewKey: '');
static const String zanoAssetId = 'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a';
late final Box<ZanoAsset> zanoAssetsBox; late final Box<ZanoAsset> zanoAssetsBox;
List<ZanoAsset> get zanoAssets => zanoAssetsBox.values.toList(); List<ZanoAsset> get zanoAssets => zanoAssetsBox.values.toList();
@ -128,45 +130,64 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
@override @override
Future<PendingTransaction> createTransaction(Object credentials) async { Future<PendingTransaction> createTransaction(Object credentials) async {
final creds = credentials as ZanoTransactionCredentials; credentials as ZanoTransactionCredentials;
final outputs = creds.outputs; bool isZano() => credentials.currency == CryptoCurrency.zano;
final outputs = credentials.outputs;
final hasMultiDestination = outputs.length > 1; final hasMultiDestination = outputs.length > 1;
final unlockedBalance = balance[CryptoCurrency.zano]?.unlocked ?? 0; final unlockedBalanceZano = BigInt.from(balance[CryptoCurrency.zano]?.unlocked ?? 0);
final fee = calculateEstimatedFee(creds.priority); final unlockedBalanceCurrency = BigInt.from(balance[credentials.currency]?.unlocked ?? 0);
final fee = BigInt.from(calculateEstimatedFee(credentials.priority));
late BigInt totalAmount;
void checkForEnoughBalances() {
if (isZano()) {
if (totalAmount + fee > unlockedBalanceZano) {
throw ZanoTransactionCreationException(
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(totalAmount + fee)} ZANO, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceZano)} ZANO).");
}
} else {
if (fee > unlockedBalanceZano) {
throw ZanoTransactionCreationException(
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(fee)} ZANO, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceZano)} ZANO).");
}
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}).");
}
}
}
final assetId = isZano() ? zanoAssetId : (currency as ZanoAsset).assetId;
late List<Destination> destinations; late List<Destination> destinations;
if (hasMultiDestination) { if (hasMultiDestination) {
if (outputs.any((output) => output.sendAll || (output.formattedCryptoAmount ?? 0) <= 0)) { if (outputs.any((output) => output.sendAll || (output.formattedCryptoAmount ?? 0) <= 0)) {
throw ZanoTransactionCreationException("You don't have enough coins."); throw ZanoTransactionCreationException("You don't have enough coins.");
} }
final int totalAmount = outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); totalAmount = outputs.fold(BigInt.zero, (acc, value) => acc + BigInt.from(value.formattedCryptoAmount ?? 0));
if (totalAmount + fee > unlockedBalance) { checkForEnoughBalances();
throw ZanoTransactionCreationException(
"You don't have enough coins (required: ${moneroAmountToString(amount: totalAmount + fee)}, unlocked ${moneroAmountToString(amount: unlockedBalance)}).");
}
destinations = outputs destinations = outputs
.map((output) => Destination( .map((output) => Destination(
amount: output.formattedCryptoAmount ?? 0, amount: BigInt.from(output.formattedCryptoAmount ?? 0),
address: output.isParsedAddress ? output.extractedAddress! : output.address, address: output.isParsedAddress ? output.extractedAddress! : output.address,
assetId: "defaultAsssetId", assetId: assetId,
)) ))
.toList(); .toList();
} else { } else {
final output = outputs.first; final output = outputs.first;
late int amount;
if (output.sendAll) { if (output.sendAll) {
amount = unlockedBalance - fee; if (isZano()) {
totalAmount = unlockedBalanceZano - fee;
} else {
totalAmount = unlockedBalanceCurrency;
}
} else { } else {
amount = output.formattedCryptoAmount!; totalAmount = BigInt.from(output.formattedCryptoAmount!);
}
if (amount + fee > unlockedBalance) {
throw ZanoTransactionCreationException(
"You don't have enough coins (required: ${moneroAmountToString(amount: amount + fee)}, unlocked ${moneroAmountToString(amount: unlockedBalance)}).");
} }
checkForEnoughBalances();
destinations = [ destinations = [
Destination( Destination(
amount: amount, amount: totalAmount,
address: output.isParsedAddress ? output.extractedAddress! : output.address, address: output.isParsedAddress ? output.extractedAddress! : output.address,
assetId: "defaultAsssetId", assetId: assetId,
) )
]; ];
} }
@ -178,18 +199,27 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
destinations: destinations, destinations: destinations,
fee: fee, fee: fee,
comment: outputs.first.note ?? '', comment: outputs.first.note ?? '',
assetId: assetId,
ticker: credentials.currency.title,
amount: totalAmount,
); );
} }
@override @override
Future<Map<String, ZanoTransactionInfo>> fetchTransactions() async { Future<Map<String, ZanoTransactionInfo>> fetchTransactions() async {
try { try {
await _refreshTransactions(); transfers = await getRecentTxsAndInfo();
return history.map<ZanoTransactionInfo>((history) => ZanoTransactionInfo.fromHistory(history)).fold<Map<String, ZanoTransactionInfo>>( return Map.fromIterable(
<String, ZanoTransactionInfo>{}, transfers,
(Map<String, ZanoTransactionInfo> acc, ZanoTransactionInfo tx) { key: (item) => (item as Transfer).txHash,
acc[tx.id] = tx; value: (item) {
return acc; item as Transfer;
if (item.subtransfers.first.assetId == zanoAssetId) {
return ZanoTransactionInfo.fromTransfer(item, 'ZANO');
} else {
final tokenSymbol = zanoAssets.firstWhere((element) => element.assetId == item.subtransfers.first.assetId).ticker;
return ZanoTransactionInfo.fromTransfer(item, tokenSymbol);
}
}, },
); );
} catch (e) { } catch (e) {
@ -286,7 +316,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
_cachedBlockchainHeight = 0; _cachedBlockchainHeight = 0;
_lastKnownBlockHeight = 0; _lastKnownBlockHeight = 0;
_initialSyncHeight = 0; _initialSyncHeight = 0;
_updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: /*1200*/5000), (_) async { _updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: /*1200*/ 5000), (_) async {
/*if (isNewTransactionExist()) { /*if (isNewTransactionExist()) {
onNewTransaction?.call(); onNewTransaction?.call();
}*/ }*/
@ -468,14 +498,6 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
} }
} }
Future<void> _refreshTransactions() async {
try {
history = await getRecentTxsAndInfo();
} catch (e) {
print(e.toString());
}
}
void _updateSyncProgress(GetWalletStatusResult walletStatus) { void _updateSyncProgress(GetWalletStatusResult walletStatus) {
final syncHeight = walletStatus.currentWalletHeight; final syncHeight = walletStatus.currentWalletHeight;
if (_initialSyncHeight <= 0) { if (_initialSyncHeight <= 0) {

View file

@ -3,12 +3,12 @@ import 'dart:convert';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_zano/api/api_calls.dart'; import 'package:cw_zano/api/api_calls.dart';
import 'package:cw_zano/api/model/add_remove_assets_whitelist_params.dart'; import 'package:cw_zano/api/model/add_remove_assets_whitelist_params.dart';
import 'package:cw_zano/api/model/asset_descriptor.dart';
import 'package:cw_zano/api/model/get_recent_txs_and_info_params.dart'; import 'package:cw_zano/api/model/get_recent_txs_and_info_params.dart';
import 'package:cw_zano/api/model/get_wallet_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/get_wallet_status_result.dart';
import 'package:cw_zano/api/model/history.dart'; import 'package:cw_zano/api/model/transfer.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/zano_asset.dart';
import 'package:flutter/foundation.dart';
import 'api/model/store_result.dart'; import 'api/model/store_result.dart';
@ -150,9 +150,10 @@ mixin ZanoWalletApi {
} }
} }
Future<List<History>> getRecentTxsAndInfo() async { Future<List<Transfer>> getRecentTxsAndInfo() async {
try { try {
final json = await invokeMethod('get_recent_txs_and_info', GetRecentTxsAndInfoParams(offset: 0, count: 30)); final json = await invokeMethod('get_recent_txs_and_info', GetRecentTxsAndInfoParams(offset: 0, count: 30));
debugPrint('get_recent_txs_and_info $json');
final map = jsonDecode(json) as Map<String, dynamic>?; final map = jsonDecode(json) as Map<String, dynamic>?;
_checkForErrors(map); _checkForErrors(map);
final transfers = map?['result']?['result']?['transfers'] as List<dynamic>?; final transfers = map?['result']?['result']?['transfers'] as List<dynamic>?;
@ -160,7 +161,7 @@ mixin ZanoWalletApi {
print('get_recent_txs_and_info empty transfers'); print('get_recent_txs_and_info empty transfers');
return []; return [];
} }
return transfers.map((e) => History.fromJson(e as Map<String, dynamic>)).toList(); return transfers.map((e) => Transfer.fromJson(e as Map<String, dynamic>)).toList();
} catch (e) { } catch (e) {
print(e); print(e);
return []; return [];

View file

@ -10,7 +10,6 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cw_zano/api/api_calls.dart' as calls;
import 'package:cw_zano/api/api_calls.dart'; import 'package:cw_zano/api/api_calls.dart';
import 'package:cw_zano/api/consts.dart'; import 'package:cw_zano/api/consts.dart';
import 'package:cw_zano/api/exceptions/already_exists_exception.dart'; import 'package:cw_zano/api/exceptions/already_exists_exception.dart';
@ -21,6 +20,7 @@ import 'package:cw_zano/api/model/create_wallet_result.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/zano_asset.dart';
import 'package:cw_zano/zano_balance.dart'; import 'package:cw_zano/zano_balance.dart';
import 'package:cw_zano/zano_wallet.dart'; import 'package:cw_zano/zano_wallet.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -71,8 +71,8 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
final wallet = ZanoWallet(credentials.walletInfo!); final wallet = ZanoWallet(credentials.walletInfo!);
await wallet.connectToNode(node: Node()); await wallet.connectToNode(node: Node());
final path = await pathForWallet(name: credentials.name, type: getType()); final path = await pathForWallet(name: credentials.name, type: getType());
final result = ApiCalls.createWallet(language: '', path: path, password: credentials.password!); final json = ApiCalls.createWallet(language: '', path: path, password: credentials.password!);
final map = json.decode(result) as Map<String, dynamic>; final map = jsonDecode(json) as Map<String, dynamic>;
_checkForCreateWalletError(map); _checkForCreateWalletError(map);
final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>); final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>);
_parseCreateWalletResult(createWalletResult, wallet); _parseCreateWalletResult(createWalletResult, wallet);
@ -111,9 +111,9 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
final walletInfo = walletInfoSource.values.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!; final walletInfo = walletInfoSource.values.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
final wallet = ZanoWallet(walletInfo); final wallet = ZanoWallet(walletInfo);
await wallet.connectToNode(node: Node()); await wallet.connectToNode(node: Node());
final result = wallet.loadWallet(path, password); final json = wallet.loadWallet(path, password);
print('load wallet result $result'); debugPrint('load wallet result $json');
final map = json.decode(result) as Map<String, dynamic>; final map = jsonDecode(json) as Map<String, dynamic>;
_checkForCreateWalletError(map); _checkForCreateWalletError(map);
final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>); final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>);
_parseCreateWalletResult(createWalletResult, wallet); _parseCreateWalletResult(createWalletResult, wallet);
@ -144,6 +144,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
wallet.walletAddresses.address = result.wi.address; wallet.walletAddresses.address = result.wi.address;
for (final item in result.wi.balances) { for (final item in result.wi.balances) {
if (item.assetInfo.ticker == 'ZANO') { if (item.assetInfo.ticker == 'ZANO') {
//wallet.zanoAssetId = item.assetInfo.assetId;
wallet.balance[CryptoCurrency.zano] = ZanoBalance(total: item.total, unlocked: item.unlocked); wallet.balance[CryptoCurrency.zano] = ZanoBalance(total: item.total, unlocked: item.unlocked);
} else { } else {
for (final asset in wallet.balance.keys) { for (final asset in wallet.balance.keys) {
@ -154,7 +155,7 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
} }
} }
if (result.recentHistory.history != null) { if (result.recentHistory.history != null) {
wallet.history = result.recentHistory.history!; wallet.transfers = result.recentHistory.history!;
} }
} }
@ -197,8 +198,8 @@ class ZanoWalletService extends WalletService<ZanoNewWalletCredentials, ZanoRest
final wallet = ZanoWallet(credentials.walletInfo!); final wallet = ZanoWallet(credentials.walletInfo!);
await wallet.connectToNode(node: Node()); await wallet.connectToNode(node: Node());
final path = await pathForWallet(name: credentials.name, type: getType()); final path = await pathForWallet(name: credentials.name, type: getType());
final result = ApiCalls.restoreWalletFromSeed(path: path, password: credentials.password!, seed: credentials.mnemonic); final json = ApiCalls.restoreWalletFromSeed(path: path, password: credentials.password!, seed: credentials.mnemonic);
final map = json.decode(result) as Map<String, dynamic>; final map = jsonDecode(json) as Map<String, dynamic>;
if (map['result'] != null) { if (map['result'] != null) {
final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>); final createWalletResult = CreateWalletResult.fromJson(map['result'] as Map<String, dynamic>);
_parseCreateWalletResult(createWalletResult, wallet); _parseCreateWalletResult(createWalletResult, wallet);

View file

@ -185,7 +185,7 @@ packages:
source: hosted source: hosted
version: "2.2.4" version: "2.2.4"
decimal: decimal:
dependency: transitive dependency: "direct main"
description: description:
name: decimal name: decimal
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21" sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"

View file

@ -18,6 +18,7 @@ dependencies:
mobx: ^2.0.7+4 mobx: ^2.0.7+4
flutter_mobx: ^2.0.6+1 flutter_mobx: ^2.0.6+1
intl: ^0.18.0 intl: ^0.18.0
decimal: ^2.3.3
cw_core: cw_core:
path: ../cw_core path: ../cw_core

View file

@ -116,7 +116,7 @@ class TransactionListItem extends ActionListItem with Keyable {
); );
break; break;
case WalletType.zano: case WalletType.zano:
final asset = zano!.assetOfTransaction(transaction); final asset = zano!.assetOfTransaction(balanceViewModel.wallet, transaction);
final price = balanceViewModel.fiatConvertationStore.prices[asset]; final price = balanceViewModel.fiatConvertationStore.prices[asset];
amount = calculateFiatAmountRaw( amount = calculateFiatAmountRaw(
cryptoAmount: zano!.formatterMoneroAmountToDouble(amount: transaction.amount), cryptoAmount: zano!.formatterMoneroAmountToDouble(amount: transaction.amount),

View file

@ -46,7 +46,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
void onWalletChange(wallet) { void onWalletChange(wallet) {
currencies = wallet.balance.keys.toList(); currencies = wallet.balance.keys.toList();
selectedCryptoCurrency = wallet.currency; selectedCryptoCurrency = wallet.currency;
hasMultipleTokens = isEVMCompatibleChain(wallet.type) || wallet.type == WalletType.solana; hasMultipleTokens = isEVMCompatibleChain(wallet.type) || wallet.type == WalletType.solana || wallet.type == WalletType.zano;
} }
SendViewModelBase( SendViewModelBase(
@ -60,7 +60,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
currencies = appStore.wallet!.balance.keys.toList(), currencies = appStore.wallet!.balance.keys.toList(),
selectedCryptoCurrency = appStore.wallet!.currency, selectedCryptoCurrency = appStore.wallet!.currency,
hasMultipleTokens = isEVMCompatibleChain(appStore.wallet!.type) || hasMultipleTokens = isEVMCompatibleChain(appStore.wallet!.type) ||
appStore.wallet!.type == WalletType.solana, appStore.wallet!.type == WalletType.solana || appStore.wallet!.type == WalletType.zano,
outputs = ObservableList<Output>(), outputs = ObservableList<Output>(),
_settingsStore = appStore.settingsStore, _settingsStore = appStore.settingsStore,
fiatFromSettings = appStore.settingsStore.fiatCurrency, fiatFromSettings = appStore.settingsStore.fiatCurrency,
@ -387,12 +387,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
return solana! return solana!
.createSolanaTransactionCredentials(outputs, currency: selectedCryptoCurrency); .createSolanaTransactionCredentials(outputs, currency: selectedCryptoCurrency);
case WalletType.zano: case WalletType.zano:
final priority = _settingsStore.priority[wallet.type]; return zano!.createZanoTransactionCredentials(
if (priority == null) { outputs: outputs, priority: priority!, currency: selectedCryptoCurrency);
throw Exception('Priority is null for wallet type: ${wallet.type}');
}
return zano!.createZanoTransactionCreationCredentials(
outputs: outputs, priority: priority, assetType: selectedCryptoCurrency.title);
default: default:
throw Exception('Unexpected wallet type: ${wallet.type}'); throw Exception('Unexpected wallet type: ${wallet.type}');
} }

View file

@ -80,21 +80,18 @@ class CWZano extends Zano {
}*/ }*/
List<ZanoAsset> getZanoAssets(WalletBase wallet) { List<ZanoAsset> getZanoAssets(WalletBase wallet) {
final zanoWallet = wallet as ZanoWallet; wallet as ZanoWallet;
return zanoWallet.zanoAssets; return wallet.zanoAssets;
} }
@override @override
Future<CryptoCurrency> addZanoAssetById(WalletBase wallet, String assetId) async => Future<CryptoCurrency> addZanoAssetById(WalletBase wallet, String assetId) async => await (wallet as ZanoWallet).addZanoAssetById(assetId);
await (wallet as ZanoWallet).addZanoAssetById(assetId);
@override @override
Future<void> addZanoAsset(WalletBase wallet, CryptoCurrency token) async => Future<void> addZanoAsset(WalletBase wallet, CryptoCurrency token) async => await (wallet as ZanoWallet).addRemoveZanoAsset(token as ZanoAsset);
await (wallet as ZanoWallet).addRemoveZanoAsset(token as ZanoAsset);
@override @override
Future<void> deleteZanoAsset(WalletBase wallet, CryptoCurrency token) async => Future<void> deleteZanoAsset(WalletBase wallet, CryptoCurrency token) async => await (wallet as ZanoWallet).deleteZanoAsset(token as ZanoAsset);
await (wallet as ZanoWallet).deleteZanoAsset(token as ZanoAsset);
@override @override
Future<ZanoAsset?> getZanoAsset(WalletBase wallet, String assetId) async { Future<ZanoAsset?> getZanoAsset(WalletBase wallet, String assetId) async {
@ -136,12 +133,20 @@ class CWZano extends Zano {
@override @override
WalletCredentials createZanoRestoreWalletFromKeysCredentials( WalletCredentials createZanoRestoreWalletFromKeysCredentials(
{required String name, required String spendKey, required String viewKey, required String address, required String password, required String language, required int height}) { {required String name,
return ZanoRestoreWalletFromKeysCredentials(name: name, spendKey: spendKey, viewKey: viewKey, address: address, password: password, language: language, height: height); required String spendKey,
required String viewKey,
required String address,
required String password,
required String language,
required int height}) {
return ZanoRestoreWalletFromKeysCredentials(
name: name, spendKey: spendKey, viewKey: viewKey, address: address, password: password, language: language, height: height);
} }
@override @override
WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}) { WalletCredentials createZanoRestoreWalletFromSeedCredentials(
{required String name, required String password, required int height, required String mnemonic}) {
return ZanoRestoreWalletFromSeedCredentials(name: name, password: password, height: height, mnemonic: mnemonic); return ZanoRestoreWalletFromSeedCredentials(name: name, password: password, height: height, mnemonic: mnemonic);
} }
@ -163,21 +168,22 @@ class CWZano extends Zano {
} }
@override @override
Object createZanoTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority, required String assetType}) { Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency}) {
return ZanoTransactionCredentials( return ZanoTransactionCredentials(
outputs: outputs outputs: outputs
.map((out) => OutputInfo( .map((out) => OutputInfo(
fiatAmount: out.fiatAmount, fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount, cryptoAmount: out.cryptoAmount,
address: out.address, address: out.address,
note: out.note, note: out.note,
sendAll: out.sendAll, sendAll: out.sendAll,
extractedAddress: out.extractedAddress, extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress, isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount)) formattedCryptoAmount: out.formattedCryptoAmount))
.toList(), .toList(),
priority: priority as MoneroTransactionPriority, priority: priority as MoneroTransactionPriority,
assetType: assetType); currency: currency,
);
} }
@override @override
@ -231,10 +237,13 @@ class CWZano extends Zano {
// } // }
@override @override
CryptoCurrency assetOfTransaction(TransactionInfo tx) { CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo transaction) {
final transaction = tx as ZanoTransactionInfo; transaction as ZanoTransactionInfo;
final asset = CryptoCurrency.fromString(transaction.assetType); if (transaction.tokenSymbol == CryptoCurrency.zano.title) {
return asset; return CryptoCurrency.zano;
}
wallet as ZanoWallet;
return wallet.zanoAssets.firstWhere((element) => element.ticker == transaction.tokenSymbol);
} }
String getZanoAssetAddress(CryptoCurrency asset) => (asset as ZanoAsset).assetId; String getZanoAssetAddress(CryptoCurrency asset) => (asset as ZanoAsset).assetId;

View file

@ -104,7 +104,7 @@ abstract class Zano {
WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic}); WalletCredentials createZanoRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic});
WalletCredentials createZanoNewWalletCredentials({required String name, String password}); WalletCredentials createZanoNewWalletCredentials({required String name, String password});
Map<String, String> getKeys(Object wallet); Map<String, String> getKeys(Object wallet);
Object createZanoTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority, required String assetType}); Object createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency});
String formatterMoneroAmountToString({required int amount}); String formatterMoneroAmountToString({required int amount});
double formatterMoneroAmountToDouble({required int amount}); double formatterMoneroAmountToDouble({required int amount});
int formatterMoneroParseAmount({required String amount}); int formatterMoneroParseAmount({required String amount});
@ -113,7 +113,7 @@ abstract class Zano {
void onStartup(); void onStartup();
int getTransactionInfoAccountId(TransactionInfo tx); int getTransactionInfoAccountId(TransactionInfo tx);
WalletService createZanoWalletService(Box<WalletInfo> walletInfoSource); WalletService createZanoWalletService(Box<WalletInfo> walletInfoSource);
CryptoCurrency assetOfTransaction(TransactionInfo tx); CryptoCurrency assetOfTransaction(WalletBase wallet, TransactionInfo tx);
List<ZanoAsset> getZanoAssets(WalletBase wallet); List<ZanoAsset> getZanoAssets(WalletBase wallet);
String getZanoAssetAddress(CryptoCurrency asset); String getZanoAssetAddress(CryptoCurrency asset);
Future<void> addZanoAsset(WalletBase wallet, CryptoCurrency token); Future<void> addZanoAsset(WalletBase wallet, CryptoCurrency token);