whitelists, some refactoring

This commit is contained in:
leo 2024-03-19 15:51:08 +00:00
parent 5af75aa7ad
commit 662aba5d72
26 changed files with 240 additions and 160 deletions

View file

@ -1,9 +0,0 @@
class AddRemoveAssetsWhitelistParams {
final String assetId;
AddRemoveAssetsWhitelistParams({required this.assetId});
Map<String, dynamic> toJson() => {
'asset_id': assetId,
};
}

View file

@ -0,0 +1,9 @@
class AssetIdParams {
final String assetId;
AssetIdParams({required this.assetId});
Map<String, dynamic> toJson() => {
'asset_id': assetId,
};
}

View file

@ -1,6 +1,6 @@
import 'package:cw_core/amount_converter.dart'; import 'package:cw_core/amount_converter.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/model/zano_asset.dart';
class Balance { class Balance {
final ZanoAsset assetInfo; final ZanoAsset assetInfo;

View file

@ -0,0 +1,13 @@
import 'dart:convert';
class ProxyToDaemonParams {
final String body;
final String uri;
ProxyToDaemonParams({required this.body, required this.uri});
Map<String, dynamic> toJson() => {
'base64_body': base64Encode(utf8.encode(body)),
'uri': uri,
};
}

View file

@ -0,0 +1,13 @@
import 'dart:convert';
class ProxyToDaemonResult {
final String body;
final int responseCode;
ProxyToDaemonResult({required this.body, required this.responseCode});
factory ProxyToDaemonResult.fromJson(Map<String, dynamic> json) => ProxyToDaemonResult(
body: utf8.decode(base64Decode(json['base64_body'] as String? ?? '')),
responseCode: json['response_code'] as int? ?? 0,
);
}

View file

@ -21,7 +21,7 @@ class TransferParams {
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'destinations': destinations, 'destinations': destinations,
'fee': fee, 'fee': fee.toInt(),
'mixin': mixin, 'mixin': mixin,
'payment_id': paymentId, 'payment_id': paymentId,
'comment': comment, 'comment': comment,

View file

@ -1,5 +1,5 @@
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/model/zano_asset.dart';
class DefaultZanoAssets { class DefaultZanoAssets {
final List<ZanoAsset> _defaultAssets = [ final List<ZanoAsset> _defaultAssets = [

View file

@ -59,6 +59,16 @@ class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
decimals: decimalPoint, decimals: decimalPoint,
); );
// ZanoAsset.copyWithCurrency(CryptoCurrency other, String? icon, String? tag, String? assetId, {bool enabled = false}):
// ZanoAsset(assetId: assetId, );
// // this.fullName = other.fullName ?? other.title,
// // this.ticker = other.title,
// // this.decimalPoint = other.decimals,
// // this.assetId = assetId,
// // this.iconPath = icon,
// // this.tag = tag,
// // this._enabled = enabled;
ZanoAsset.copyWith(ZanoAsset other, String? icon, String? tag, {String? assetId, bool enabled = false}) ZanoAsset.copyWith(ZanoAsset other, String? icon, String? tag, {String? assetId, bool enabled = false})
: this.fullName = other.fullName, : this.fullName = other.fullName,
this.ticker = other.ticker, this.ticker = other.ticker,

View file

@ -12,12 +12,23 @@ class ZanoFormatter {
..minimumFractionDigits = 1; ..minimumFractionDigits = 1;
static Decimal _intDivision({required int amount, required BigInt divider}) => (Decimal.fromInt(amount) / Decimal.fromBigInt(divider)).toDecimal(); 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 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( static String intAmountToString(int amount, [int decimalPoint = defaultDecimalPoint]) => numberFormat.format(
_intDivision(amount: amount, divider: BigInt.from(pow(10, decimalPoint))), DecimalIntl(
),); _intDivision(
static String bigIntAmountToString(BigInt amount, [int decimalPoint = defaultDecimalPoint]) => numberFormat.format(DecimalIntl( amount: amount,
_bigIntDivision(amount: amount, divider: BigInt.from(pow(10, decimalPoint))), divider: BigInt.from(pow(10, decimalPoint)),
),); ),
),
).replaceAll(',', '');
static String bigIntAmountToString(BigInt amount, [int decimalPoint = defaultDecimalPoint]) => numberFormat.format(
DecimalIntl(
_bigIntDivision(
amount: amount,
divider: BigInt.from(pow(10, decimalPoint)),
),
),
).replaceAll(',', '');
} }

View file

@ -1,7 +1,7 @@
import 'dart:core'; import 'dart:core';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_zano/zano_transaction_info.dart'; import 'package:cw_zano/model/zano_transaction_info.dart';
part 'zano_transaction_history.g.dart'; part 'zano_transaction_history.g.dart';

View file

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cw_zano/api/api_calls.dart'; import 'package:cw_zano/api/api_calls.dart';
import 'package:cw_zano/api/model/get_address_info_result.dart'; import 'package:cw_zano/api/model/get_address_info_result.dart';
import 'package:cw_zano/zano_wallet_api.dart';
class ZanoUtils { class ZanoUtils {
static bool validateAddress(String address) { static bool validateAddress(String address) {

View file

@ -15,15 +15,15 @@ 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/transfer.dart'; import 'package:cw_zano/api/model/transfer.dart';
import 'package:cw_zano/api/model/zano_wallet_keys.dart'; import 'package:cw_zano/model/zano_wallet_keys.dart';
import 'package:cw_zano/exceptions/zano_transaction_creation_exception.dart'; import 'package:cw_zano/model/zano_transaction_creation_exception.dart';
import 'package:cw_zano/pending_zano_transaction.dart'; import 'package:cw_zano/model/pending_zano_transaction.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/model/zano_asset.dart';
import 'package:cw_zano/zano_balance.dart'; import 'package:cw_zano/model/zano_balance.dart';
import 'package:cw_zano/zano_formatter.dart'; import 'package:cw_zano/zano_formatter.dart';
import 'package:cw_zano/zano_transaction_credentials.dart'; import 'package:cw_zano/model/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/model/zano_transaction_info.dart';
import 'package:cw_zano/zano_wallet_addresses.dart'; import 'package:cw_zano/zano_wallet_addresses.dart';
import 'package:cw_zano/zano_wallet_api.dart'; import 'package:cw_zano/zano_wallet_api.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -130,7 +130,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
@override @override
Future<PendingTransaction> createTransaction(Object credentials) async { Future<PendingTransaction> createTransaction(Object credentials) async {
credentials as ZanoTransactionCredentials; credentials as ZanoTransactionCredentials;
bool isZano() => credentials.currency == CryptoCurrency.zano; final isZano = credentials.currency == CryptoCurrency.zano;
final outputs = credentials.outputs; final outputs = credentials.outputs;
final hasMultiDestination = outputs.length > 1; final hasMultiDestination = outputs.length > 1;
final unlockedBalanceZano = BigInt.from(balance[CryptoCurrency.zano]?.unlocked ?? 0); final unlockedBalanceZano = BigInt.from(balance[CryptoCurrency.zano]?.unlocked ?? 0);
@ -138,7 +138,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
final fee = BigInt.from(calculateEstimatedFee(credentials.priority)); final fee = BigInt.from(calculateEstimatedFee(credentials.priority));
late BigInt totalAmount; late BigInt totalAmount;
void checkForEnoughBalances() { void checkForEnoughBalances() {
if (isZano()) { if (isZano) {
if (totalAmount + fee > unlockedBalanceZano) { if (totalAmount + fee > unlockedBalanceZano) {
throw ZanoTransactionCreationException( throw ZanoTransactionCreationException(
"You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(totalAmount + fee)} ZANO, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceZano)} ZANO)."); "You don't have enough coins (required: ${ZanoFormatter.bigIntAmountToString(totalAmount + fee)} ZANO, unlocked ${ZanoFormatter.bigIntAmountToString(unlockedBalanceZano)} ZANO).");
@ -155,7 +155,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
} }
} }
final assetId = isZano() ? zanoAssetId : (currency as ZanoAsset).assetId; final assetId = isZano ? zanoAssetId : (credentials.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)) {
@ -173,7 +173,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
} else { } else {
final output = outputs.first; final output = outputs.first;
if (output.sendAll) { if (output.sendAll) {
if (isZano()) { if (isZano) {
totalAmount = unlockedBalanceZano - fee; totalAmount = unlockedBalanceZano - fee;
} else { } else {
totalAmount = unlockedBalanceCurrency; totalAmount = unlockedBalanceCurrency;
@ -413,7 +413,7 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
return asset; return asset;
} }
Future<void> addRemoveZanoAsset(ZanoAsset asset) async { Future<void> changeZanoAssetAvailability(ZanoAsset asset) async {
String? iconPath; String? iconPath;
try { try {
iconPath = CryptoCurrency.all.firstWhere((element) => element.title.toUpperCase() == asset.title.toUpperCase()).iconPath; iconPath = CryptoCurrency.all.firstWhere((element) => element.title.toUpperCase() == asset.title.toUpperCase()).iconPath;
@ -422,15 +422,13 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
if (asset.enabled) { if (asset.enabled) {
final assetDescriptor = await addAssetsWhitelist(asset.assetId); final assetDescriptor = await addAssetsWhitelist(asset.assetId);
if (assetDescriptor == null) { if (assetDescriptor == null) {
print('error adding zano asset'); throw 'error adding zano asset';
return;
} }
balance[asset] = ZanoBalance(total: 0, unlocked: 0, decimalPoint: asset.decimalPoint); balance[asset] = ZanoBalance(total: 0, unlocked: 0, decimalPoint: asset.decimalPoint);
} else { } else {
final result = await removeAssetsWhitelist(asset.assetId); final result = await removeAssetsWhitelist(asset.assetId);
if (result == false) { if (result == false) {
print('error removing zano asset'); throw 'error removing zano asset';
return;
} }
balance.removeWhere((key, _) => key is ZanoAsset && key.assetId == asset.assetId); balance.removeWhere((key, _) => key is ZanoAsset && key.assetId == asset.assetId);
} }
@ -439,12 +437,12 @@ abstract class ZanoWalletBase extends WalletBase<ZanoBalance, ZanoTransactionHis
Future<void> deleteZanoAsset(ZanoAsset asset) async { Future<void> deleteZanoAsset(ZanoAsset asset) async {
final result = await removeAssetsWhitelist(asset.assetId); final result = await removeAssetsWhitelist(asset.assetId);
if (result == false) return; if (result == false) return;
await asset.delete(); if (asset.isInBox) await asset.delete();
balance.removeWhere((key, _) => key is ZanoAsset && key.assetId == asset.assetId); balance.removeWhere((key, _) => key is ZanoAsset && key.assetId == asset.assetId);
} }
Future<ZanoAsset?> getZanoAsset(String assetId) async { Future<ZanoAsset?> getZanoAsset(String assetId) async {
return null; return await getAssetInfo(assetId);
} }
// List<ZanoTransactionInfo> _getAllTransactions(dynamic _) => // List<ZanoTransactionInfo> _getAllTransactions(dynamic _) =>

View file

@ -2,12 +2,15 @@ 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/asset_id_params.dart';
import 'package:cw_zano/api/model/get_address_info_result.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/proxy_to_daemon_params.dart';
import 'package:cw_zano/api/model/proxy_to_daemon_result.dart';
import 'package:cw_zano/api/model/transfer.dart'; import 'package:cw_zano/api/model/transfer.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/model/zano_asset.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'api/model/store_result.dart'; import 'api/model/store_result.dart';
@ -47,10 +50,10 @@ mixin ZanoWalletApi {
final result = GetWalletInfoResult.fromJson(jsonDecode(json) as Map<String, dynamic>); final result = GetWalletInfoResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
switch (_logType) { switch (_logType) {
case _LogType.json: case _LogType.json:
print('get_wallet_info $json'); debugPrint('get_wallet_info $json');
break; break;
case _LogType.simple: case _LogType.simple:
print('get_wallet_info got ${result.wi.balances.length} balances: ${result.wi.balances}'); debugPrint('get_wallet_info got ${result.wi.balances.length} balances: ${result.wi.balances}');
} }
return result; return result;
@ -61,10 +64,11 @@ mixin ZanoWalletApi {
final status = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>); final status = GetWalletStatusResult.fromJson(jsonDecode(json) as Map<String, dynamic>);
switch (_logType) { switch (_logType) {
case _LogType.json: case _LogType.json:
print('get_wallet_status $json'); debugPrint('get_wallet_status $json');
break; break;
case _LogType.simple: case _LogType.simple:
print('get_wallet_status connected: ${status.isDaemonConnected} in refresh: ${status.isInLongRefresh} wallet state: ${status.walletState}'); debugPrint(
'get_wallet_status connected: ${status.isDaemonConnected} in refresh: ${status.isInLongRefresh} wallet state: ${status.walletState}');
} }
return status; return status;
} }
@ -91,13 +95,19 @@ mixin ZanoWalletApi {
Future<List<ZanoAsset>> getAssetsWhitelist() async { Future<List<ZanoAsset>> getAssetsWhitelist() async {
try { try {
final json = await invokeMethod('assets_whitelist_get', '{}'); final json = await invokeMethod('assets_whitelist_get', '{}');
if (_logType == _LogType.json) print('assets_whitelist_get $json'); /*if (_logType == _LogType.json)*/ debugPrint('assets_whitelist_get $json');
final map = jsonDecode(json) as Map<String, dynamic>?; final map = jsonDecode(json) as Map<String, dynamic>?;
_checkForErrors(map); _checkForErrors(map);
final assets = map?['result']?['result']?['assets'] as List<dynamic>?; List<ZanoAsset> assets(String type) =>
final result = assets?.map((e) => ZanoAsset.fromJson(e as Map<String, dynamic>)).toList(); (map?['result']?['result']?[type] as List<dynamic>?)?.map((e) => ZanoAsset.fromJson(e as Map<String, dynamic>)).toList() ?? [];
if (_logType == _LogType.simple) print('assets_whitelist_get got ${result?.length ?? 0} assets: $result'); final localWhitelist = assets('local_whitelist');
return result ?? []; final globalWhitelist = assets('global_whitelist');
final ownAssets = assets('own_assets');
if (_logType == _LogType.simple)
print('assets_whitelist_get got local whitelist: ${localWhitelist.length} ($localWhitelist); '
'global whitelist: ${globalWhitelist.length} ($globalWhitelist); '
'own assets: ${ownAssets.length} ($ownAssets)');
return [...localWhitelist, ...globalWhitelist, ...ownAssets];
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
return []; return [];
@ -106,7 +116,7 @@ mixin ZanoWalletApi {
Future<ZanoAsset?> addAssetsWhitelist(String assetId) async { Future<ZanoAsset?> addAssetsWhitelist(String assetId) async {
try { try {
final json = await invokeMethod('assets_whitelist_add', AddRemoveAssetsWhitelistParams(assetId: assetId)); final json = await invokeMethod('assets_whitelist_add', AssetIdParams(assetId: assetId));
if (_logType == _LogType.json) print('assets_whitelist_add $assetId $json'); if (_logType == _LogType.json) print('assets_whitelist_add $assetId $json');
final map = jsonDecode(json) as Map<String, dynamic>?; final map = jsonDecode(json) as Map<String, dynamic>?;
_checkForErrors(map); _checkForErrors(map);
@ -126,7 +136,7 @@ mixin ZanoWalletApi {
Future<bool> removeAssetsWhitelist(String assetId) async { Future<bool> removeAssetsWhitelist(String assetId) async {
try { try {
final json = await invokeMethod('assets_whitelist_remove', AddRemoveAssetsWhitelistParams(assetId: assetId)); final json = await invokeMethod('assets_whitelist_remove', AssetIdParams(assetId: assetId));
if (_logType == _LogType.json) print('assets_whitelist_remove $assetId $json'); if (_logType == _LogType.json) print('assets_whitelist_remove $assetId $json');
final map = jsonDecode(json) as Map<String, dynamic>?; final map = jsonDecode(json) as Map<String, dynamic>?;
_checkForErrors(map); _checkForErrors(map);
@ -138,6 +148,36 @@ mixin ZanoWalletApi {
} }
} }
Future<ProxyToDaemonResult?> _proxyToDaemon(String uri, String body) async {
final json = await invokeMethod('proxy_to_daemon', ProxyToDaemonParams(body: body, uri: uri));
final map = jsonDecode(json) as Map<String, dynamic>?;
_checkForErrors(map);
return ProxyToDaemonResult.fromJson(map!['result']['result'] as Map<String, dynamic>);
}
Future<ZanoAsset?> getAssetInfo(String assetId) async {
final methodName = 'get_asset_info';
final params = AssetIdParams(assetId: assetId);
final result = await _proxyToDaemon('/json_rpc', '{"method": "$methodName","params": ${jsonEncode(params)}}');
if (_logType == _LogType.json) print('$methodName $assetId ${result?.body}');
if (result == null) {
debugPrint('get_asset_info empty result');
return null;
}
final map = jsonDecode(result.body) as Map<String, dynamic>?;
if (map!['error'] != null) {
if (_logType == _LogType.simple) print('get_asset_info $assetId error ${map['error']!['code']} ${map['error']!['message']}');
return null;
} else if (map['result']!['status']! == 'OK') {
final assetDescriptor = ZanoAsset.fromJson(map['result']!['asset_descriptor']! as Map<String, dynamic>);
if (_logType == _LogType.simple) print('get_asset_info $assetId ${assetDescriptor.fullName} ${assetDescriptor.ticker}');
return assetDescriptor;
} else {
if (_logType == _LogType.simple) print('get_asset_info $assetId status ${map['result']!['status']!}');
return null;
}
}
Future<StoreResult?> store() async { Future<StoreResult?> store() async {
try { try {
final json = await invokeMethod('store', '{}'); final json = await invokeMethod('store', '{}');
@ -153,7 +193,7 @@ mixin ZanoWalletApi {
Future<List<Transfer>> 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'); //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>?;
@ -168,6 +208,10 @@ mixin ZanoWalletApi {
} }
} }
GetAddressInfoResult getAddressInfo(String address) => GetAddressInfoResult.fromJson(
jsonDecode(ApiCalls.getAddressInfo(address: address)) as Map<String, dynamic>,
);
void _checkForErrors(Map<String, dynamic>? map) { void _checkForErrors(Map<String, dynamic>? map) {
if (map == null) { if (map == null) {
throw 'empty response'; throw 'empty response';

View file

@ -17,8 +17,8 @@ import 'package:cw_zano/api/exceptions/create_wallet_exception.dart';
import 'package:cw_zano/api/exceptions/restore_from_seed_exception.dart'; import 'package:cw_zano/api/exceptions/restore_from_seed_exception.dart';
import 'package:cw_zano/api/exceptions/wrong_seed_exception.dart'; import 'package:cw_zano/api/exceptions/wrong_seed_exception.dart';
import 'package:cw_zano/api/model/create_wallet_result.dart'; import 'package:cw_zano/api/model/create_wallet_result.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/model/zano_asset.dart';
import 'package:cw_zano/zano_balance.dart'; import 'package:cw_zano/model/zano_balance.dart';
import 'package:cw_zano/zano_formatter.dart'; import 'package:cw_zano/zano_formatter.dart';
import 'package:cw_zano/zano_wallet.dart'; import 'package:cw_zano/zano_wallet.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';

View file

@ -7,12 +7,12 @@ import 'package:cw_core/erc20_token.dart';
import 'package:cw_zano/zano_utils.dart'; import 'package:cw_zano/zano_utils.dart';
class AddressValidator extends TextValidator { class AddressValidator extends TextValidator {
AddressValidator({required CryptoCurrency type}) AddressValidator({required CryptoCurrency type, bool skipZanoAddressValidation = false})
: super( : super(
errorMessage: S.current.error_text_address, errorMessage: S.current.error_text_address,
useAdditionalValidation: type == CryptoCurrency.btc useAdditionalValidation: type == CryptoCurrency.btc
? (String txt) => validateAddress(address: txt, network: BitcoinNetwork.mainnet) ? (String txt) => validateAddress(address: txt, network: BitcoinNetwork.mainnet)
: type == CryptoCurrency.zano : type == CryptoCurrency.zano && !skipZanoAddressValidation
? ZanoUtils.validateAddress ? ZanoUtils.validateAddress
: null, : null,
pattern: getPattern(type), pattern: getPattern(type),
@ -125,6 +125,8 @@ class AddressValidator extends TextValidator {
return 'D([1-9a-km-zA-HJ-NP-Z]){33}'; return 'D([1-9a-km-zA-HJ-NP-Z]){33}';
case CryptoCurrency.btcln: case CryptoCurrency.btcln:
return '^(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)'; return '^(lnbc|LNBC)([0-9]{1,}[a-zA-Z0-9]+)';
case CryptoCurrency.zano:
return r'$.^'; // always false, we use additional validation then
default: default:
return '[0-9a-zA-Z]'; return '[0-9a-zA-Z]';
} }

View file

@ -13,7 +13,6 @@ import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart'; import 'package:cw_core/erc20_token.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cw_zano/zano_asset.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -196,27 +195,12 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
child: PrimaryButton( child: PrimaryButton(
onPressed: () async { onPressed: () async {
if (_formKey.currentState!.validate() && (!_showDisclaimer || _disclaimerChecked)) { if (_formKey.currentState!.validate() && (!_showDisclaimer || _disclaimerChecked)) {
if (widget.homeSettingsViewModel.walletType == WalletType.zano) { await widget.homeSettingsViewModel.addToken(Erc20Token(
if (!await widget.homeSettingsViewModel.addAsset(_contractAddressController.text)) { name: _tokenNameController.text,
await showPopUp<void>( symbol: _tokenSymbolController.text,
context: context, contractAddress: _contractAddressController.text,
builder: (BuildContext context) { decimal: int.parse(_tokenDecimalController.text),
return AlertWithOneAction( ));
alertTitle: S.current.error,
alertContent: 'Cannot add asset ${_contractAddressController.text}',
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
return;
}
} else {
await widget.homeSettingsViewModel.addToken(Erc20Token(
name: _tokenNameController.text,
symbol: _tokenSymbolController.text,
contractAddress: _contractAddressController.text,
decimal: int.parse(_tokenDecimalController.text),
));
}
} }
if (context.mounted) { if (context.mounted) {
Navigator.pop(context); Navigator.pop(context);
@ -240,9 +224,10 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
final token = await widget.homeSettingsViewModel.getToken(_contractAddressController.text); final token = await widget.homeSettingsViewModel.getToken(_contractAddressController.text);
if (token != null) { if (token != null) {
if (_tokenNameController.text.isEmpty) _tokenNameController.text = token.name; final isZano = widget.homeSettingsViewModel.walletType == WalletType.zano;
if (_tokenSymbolController.text.isEmpty) _tokenSymbolController.text = token.title; if (_tokenNameController.text.isEmpty || isZano) _tokenNameController.text = token.name;
if (_tokenDecimalController.text.isEmpty) _tokenDecimalController.text = token.decimals.toString(); if (_tokenSymbolController.text.isEmpty || isZano) _tokenSymbolController.text = token.title;
if (_tokenDecimalController.text.isEmpty || isZano) _tokenDecimalController.text = token.decimals.toString();
} }
} }
} }
@ -273,60 +258,59 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
placeholder: S.of(context).token_contract_address, placeholder: S.of(context).token_contract_address,
options: [AddressTextFieldOption.paste], options: [AddressTextFieldOption.paste],
buttonColor: Theme.of(context).hintColor, buttonColor: Theme.of(context).hintColor,
validator: AddressValidator(type: widget.homeSettingsViewModel.nativeToken), // we don't use zano addresses validations here, addresses and asset ids are difference entities
validator: AddressValidator(type: widget.homeSettingsViewModel.nativeToken, skipZanoAddressValidation: true),
onPushPasteButton: (_) { onPushPasteButton: (_) {
_pasteText(); _pasteText();
}, },
), ),
if (widget.homeSettingsViewModel.walletType != WalletType.zano) ...[ const SizedBox(height: 8),
const SizedBox(height: 8), BaseTextFormField(
BaseTextFormField( controller: _tokenNameController,
controller: _tokenNameController, focusNode: _tokenNameFocusNode,
focusNode: _tokenNameFocusNode, onSubmit: (_) => FocusScope.of(context).requestFocus(_tokenSymbolFocusNode),
onSubmit: (_) => FocusScope.of(context).requestFocus(_tokenSymbolFocusNode), textInputAction: TextInputAction.next,
textInputAction: TextInputAction.next, hintText: S.of(context).token_name,
hintText: S.of(context).token_name, validator: (text) {
validator: (text) { if (text?.isNotEmpty ?? false) {
if (text?.isNotEmpty ?? false) {
return null;
}
return S.of(context).field_required;
},
),
const SizedBox(height: 8),
BaseTextFormField(
controller: _tokenSymbolController,
focusNode: _tokenSymbolFocusNode,
onSubmit: (_) => FocusScope.of(context).requestFocus(_tokenDecimalFocusNode),
textInputAction: TextInputAction.next,
hintText: S.of(context).token_symbol,
validator: (text) {
if (text?.isNotEmpty ?? false) {
return null;
}
return S.of(context).field_required;
},
),
const SizedBox(height: 8),
BaseTextFormField(
controller: _tokenDecimalController,
focusNode: _tokenDecimalFocusNode,
textInputAction: TextInputAction.done,
hintText: S.of(context).token_decimal,
validator: (text) {
if (text?.isEmpty ?? true) {
return S.of(context).field_required;
}
if (int.tryParse(text!) == null) {
return S.of(context).invalid_input;
}
return null; return null;
}, }
),
], return S.of(context).field_required;
},
),
const SizedBox(height: 8),
BaseTextFormField(
controller: _tokenSymbolController,
focusNode: _tokenSymbolFocusNode,
onSubmit: (_) => FocusScope.of(context).requestFocus(_tokenDecimalFocusNode),
textInputAction: TextInputAction.next,
hintText: S.of(context).token_symbol,
validator: (text) {
if (text?.isNotEmpty ?? false) {
return null;
}
return S.of(context).field_required;
},
),
const SizedBox(height: 8),
BaseTextFormField(
controller: _tokenDecimalController,
focusNode: _tokenDecimalFocusNode,
textInputAction: TextInputAction.done,
hintText: S.of(context).token_decimal,
validator: (text) {
if (text?.isEmpty ?? true) {
return S.of(context).field_required;
}
if (int.tryParse(text!) == null) {
return S.of(context).invalid_input;
}
return null;
},
),
SizedBox(height: 24), SizedBox(height: 24),
], ],
), ),

View file

@ -317,6 +317,8 @@ class WalletListBodyState extends State<WalletListBody> {
return polygonIcon; return polygonIcon;
case WalletType.solana: case WalletType.solana:
return solanaIcon; return solanaIcon;
case WalletType.zano:
return zanoIcon;
default: default:
return nonWalletTypeIcon; return nonWalletTypeIcon;
} }

View file

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

View file

@ -51,7 +51,7 @@ part of 'zano.dart';
} }
}*/ }*/
class CWZanoWalletDetails extends ZanoWalletDetails { /*class CWZanoWalletDetails extends ZanoWalletDetails {
CWZanoWalletDetails(this._wallet); CWZanoWalletDetails(this._wallet);
final Object _wallet; final Object _wallet;
@ -64,14 +64,14 @@ class CWZanoWalletDetails extends ZanoWalletDetails {
// return Account(id: acc.id, label: acc.label); // return Account(id: acc.id, label: acc.label);
// } // }
@computed // @computed
@override // @override
ZanoBalance get balance { // ZanoBalance get balance {
final zanoWallet = _wallet as ZanoWallet; // final zanoWallet = _wallet as ZanoWallet;
final balance = zanoWallet.balance; // final balance = zanoWallet.balance;
return ZanoBalance(fullBalance: balance[CryptoCurrency.zano]!.total, unlockedBalance: balance[CryptoCurrency.zano]!.unlocked); // return ZanoBalance(fullBalance: balance[CryptoCurrency.zano]!.total, unlockedBalance: balance[CryptoCurrency.zano]!.unlocked);
} // }
} }*/
class CWZano extends Zano { class CWZano extends Zano {
/**@override /**@override
@ -88,7 +88,7 @@ class CWZano extends Zano {
Future<CryptoCurrency> addZanoAssetById(WalletBase wallet, String assetId) async => await (wallet as ZanoWallet).addZanoAssetById(assetId); Future<CryptoCurrency> addZanoAssetById(WalletBase wallet, String assetId) async => await (wallet as ZanoWallet).addZanoAssetById(assetId);
@override @override
Future<void> addZanoAsset(WalletBase wallet, CryptoCurrency token) async => await (wallet as ZanoWallet).addRemoveZanoAsset(token as ZanoAsset); Future<void> changeZanoAssetAvailability(WalletBase wallet, CryptoCurrency token) async => await (wallet as ZanoWallet).changeZanoAssetAvailability(token as ZanoAsset);
@override @override
Future<void> deleteZanoAsset(WalletBase wallet, CryptoCurrency token) async => await (wallet as ZanoWallet).deleteZanoAsset(token as ZanoAsset); Future<void> deleteZanoAsset(WalletBase wallet, CryptoCurrency token) async => await (wallet as ZanoWallet).deleteZanoAsset(token as ZanoAsset);
@ -105,10 +105,10 @@ class CWZano extends Zano {
return zanoWallet.transactionHistory; return zanoWallet.transactionHistory;
} }
@override // @override
ZanoWalletDetails getZanoWalletDetails(Object wallet) { // ZanoWalletDetails getZanoWalletDetails(Object wallet) {
return CWZanoWalletDetails(wallet); // return CWZanoWalletDetails(wallet);
} // }
@override @override
TransactionPriority getDefaultTransactionPriority() { TransactionPriority getDefaultTransactionPriority() {
@ -186,10 +186,10 @@ class CWZano extends Zano {
); );
} }
@override // @override
String formatterMoneroAmountToString({required int amount}) { // String formatterMoneroAmountToString({required int amount}) {
return moneroAmountToString(amount: amount); // return moneroAmountToString(amount: amount);
} // }
@override @override
double formatterMoneroAmountToDouble({required int amount}) { double formatterMoneroAmountToDouble({required int amount}) {

View file

@ -1,7 +1,7 @@
import 'package:cake_wallet/utils/language_list.dart'; import 'package:cake_wallet/utils/language_list.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_zano/zano_asset.dart'; import 'package:cw_zano/model/zano_asset.dart';
import 'package:cw_zano/zano_transaction_credentials.dart'; import 'package:cw_zano/model/zano_transaction_credentials.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';
@ -19,7 +19,7 @@ import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_zano/zano_wallet_service.dart'; import 'package:cw_zano/zano_wallet_service.dart';
import 'package:cw_zano/zano_wallet.dart'; import 'package:cw_zano/zano_wallet.dart';
import 'package:cw_zano/zano_transaction_info.dart'; import 'package:cw_zano/model/zano_transaction_info.dart';
import 'package:cw_zano/mnemonics/english.dart'; import 'package:cw_zano/mnemonics/english.dart';
part 'cw_zano.dart'; part 'cw_zano.dart';
@ -42,7 +42,7 @@ Zano? zano = CWZano();
// final String address; // final String address;
// } // }
class ZanoBalance extends Balance { /*class ZanoBalance extends Balance {
ZanoBalance({required this.fullBalance, required this.unlockedBalance}) ZanoBalance({required this.fullBalance, required this.unlockedBalance})
: formattedFullBalance = zano!.formatterMoneroAmountToString(amount: fullBalance), : formattedFullBalance = zano!.formatterMoneroAmountToString(amount: fullBalance),
formattedUnlockedBalance = formattedUnlockedBalance =
@ -67,24 +67,24 @@ class ZanoBalance extends Balance {
@override @override
String get formattedAdditionalBalance => formattedFullBalance; String get formattedAdditionalBalance => formattedFullBalance;
} }*/
abstract class ZanoWalletDetails { /*abstract class ZanoWalletDetails {
// FIX-ME: it's abstruct class // FIX-ME: it's abstruct class
// @observable // @observable
// late Account account; // late Account account;
// FIX-ME: it's abstruct class // FIX-ME: it's abstruct class
@observable @observable
late ZanoBalance balance; late ZanoBalance balance;
} }*/
abstract class Zano { abstract class Zano {
/**ZanoAccountList getAccountList(Object wallet);*/ /**ZanoAccountList getAccountList(Object wallet);*/
TransactionHistoryBase getTransactionHistory(Object wallet); TransactionHistoryBase getTransactionHistory(Object wallet);
ZanoWalletDetails getZanoWalletDetails(Object wallet); //ZanoWalletDetails getZanoWalletDetails(Object wallet);
// String getTransactionAddress(Object wallet, int accountIndex, int addressIndex); // String getTransactionAddress(Object wallet, int accountIndex, int addressIndex);
@ -105,7 +105,7 @@ abstract class Zano {
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 createZanoTransactionCredentials({required List<Output> outputs, required TransactionPriority priority, required CryptoCurrency currency}); 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});
// Account getCurrentAccount(Object wallet); // Account getCurrentAccount(Object wallet);
@ -116,7 +116,7 @@ abstract class Zano {
CryptoCurrency assetOfTransaction(WalletBase wallet, 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> changeZanoAssetAvailability(WalletBase wallet, CryptoCurrency token);
Future<CryptoCurrency> addZanoAssetById(WalletBase wallet, String assetId); Future<CryptoCurrency> addZanoAssetById(WalletBase wallet, String assetId);
Future<void> deleteZanoAsset(WalletBase wallet, CryptoCurrency token); Future<void> deleteZanoAsset(WalletBase wallet, CryptoCurrency token);
Future<CryptoCurrency?> getZanoAsset(WalletBase wallet, String contractAddress); Future<CryptoCurrency?> getZanoAsset(WalletBase wallet, String contractAddress);