diff --git a/lib/models/isar/models/blockchain_data/address.dart b/lib/models/isar/models/blockchain_data/address.dart index 25281a629..dd06ccb84 100644 --- a/lib/models/isar/models/blockchain_data/address.dart +++ b/lib/models/isar/models/blockchain_data/address.dart @@ -123,7 +123,8 @@ enum AddressType { mimbleWimble, unknown, nonWallet, - ethereum; + ethereum, + nano; String get readableName { switch (this) { @@ -143,6 +144,8 @@ enum AddressType { return "Non wallet/unknown"; case AddressType.ethereum: return "Ethereum"; + case AddressType.nano: + return "Nano"; } } } diff --git a/lib/models/isar/models/blockchain_data/address.g.dart b/lib/models/isar/models/blockchain_data/address.g.dart index 49188b395..5498a0434 100644 --- a/lib/models/isar/models/blockchain_data/address.g.dart +++ b/lib/models/isar/models/blockchain_data/address.g.dart @@ -261,6 +261,7 @@ const _AddresstypeEnumValueMap = { 'unknown': 5, 'nonWallet': 6, 'ethereum': 7, + 'nano': 8, }; const _AddresstypeValueEnumMap = { 0: AddressType.p2pkh, @@ -271,6 +272,7 @@ const _AddresstypeValueEnumMap = { 5: AddressType.unknown, 6: AddressType.nonWallet, 7: AddressType.ethereum, + 8: AddressType.nano, }; Id _addressGetId(Address object) { diff --git a/lib/services/coins/nano/nano_wallet.dart b/lib/services/coins/nano/nano_wallet.dart index a2e785afd..d275a7003 100644 --- a/lib/services/coins/nano/nano_wallet.dart +++ b/lib/services/coins/nano/nano_wallet.dart @@ -9,6 +9,8 @@ import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart' import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/services/coins/coin_service.dart'; +import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; +import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; @@ -16,6 +18,7 @@ import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/log_level_enum.dart'; import 'package:stackwallet/utilities/logger.dart'; +import 'package:tuple/tuple.dart'; import '../../../db/isar/main_db.dart'; import '../../../models/isar/models/blockchain_data/address.dart'; @@ -179,8 +182,8 @@ class NanoWallet extends CoinServiceAPI headers: headers, body: balanceBody, ); - final balanceData = jsonDecode(balanceResponse.body); + final BigInt currentBalance = BigInt.parse(balanceData["balance"].toString()); final BigInt txAmount = txData["recipientAmt"].raw as BigInt; @@ -272,7 +275,8 @@ class NanoWallet extends CoinServiceAPI @override Future estimateFeeFor(Amount amount, int feeRate) { // fees are always 0 :) - return Future.value(Amount(rawValue: BigInt.from(0), fractionDigits: 7)); + return Future.value( + Amount(rawValue: BigInt.from(0), fractionDigits: coin.decimals)); } @override @@ -297,18 +301,17 @@ class NanoWallet extends CoinServiceAPI final data = jsonDecode(response.body); _balance = Balance( total: Amount( - rawValue: (BigInt.parse(data["balance"].toString())) ~/ - BigInt.from(10).pow(23), - fractionDigits: 7), + rawValue: (BigInt.parse(data["balance"].toString()) + + BigInt.parse(data["receivable"].toString())), + fractionDigits: coin.decimals), spendable: Amount( - rawValue: BigInt.parse(data["balance"].toString()) ~/ - BigInt.from(10).pow(23), - fractionDigits: 7), - blockedTotal: Amount(rawValue: BigInt.parse("0"), fractionDigits: 30), + rawValue: BigInt.parse(data["balance"].toString()), + fractionDigits: coin.decimals), + blockedTotal: + Amount(rawValue: BigInt.parse("0"), fractionDigits: coin.decimals), pendingSpendable: Amount( - rawValue: BigInt.parse(data["receivable"].toString()) ~/ - BigInt.from(10).pow(23), - fractionDigits: 7), + rawValue: BigInt.parse(data["receivable"].toString()), + fractionDigits: coin.decimals), ); await updateCachedBalance(_balance!); } @@ -464,11 +467,12 @@ class NanoWallet extends CoinServiceAPI Future updateTransactions() async { await confirmAllReceivable(); + final String publicAddress = await getAddressFromMnemonic(); final response = await http.post(Uri.parse(getCurrentNode().host), headers: {"Content-Type": "application/json"}, body: jsonEncode({ "action": "account_history", - "account": await getAddressFromMnemonic(), + "account": publicAddress, "count": "-1", })); final data = await jsonDecode(response.body); @@ -476,14 +480,14 @@ class NanoWallet extends CoinServiceAPI if (transactions.isEmpty) { return; } else { - List transactionList = []; + List> transactionList = []; for (var tx in transactions) { var typeString = tx["type"].toString(); - TransactionType type = TransactionType.unknown; + TransactionType transactionType = TransactionType.unknown; if (typeString == "send") { - type = TransactionType.outgoing; + transactionType = TransactionType.outgoing; } else if (typeString == "receive") { - type = TransactionType.incoming; + transactionType = TransactionType.incoming; } final amount = Amount( rawValue: BigInt.parse(tx["amount"].toString()), @@ -491,30 +495,45 @@ class NanoWallet extends CoinServiceAPI ); var transaction = Transaction( - walletId: walletId, - txid: tx["hash"].toString(), - timestamp: int.parse(tx["local_timestamp"].toString()), - type: type, - subType: TransactionSubType.none, - amount: 0, - amountString: amount.toJsonString(), - fee: 0, - height: int.parse(tx["height"].toString()), - isCancelled: false, - isLelantus: false, - slateId: "", - otherData: "", - inputs: [], - outputs: [], - nonce: 0); - transactionList.add(transaction); - } - try { - await db.putTransactions(transactionList); - } catch (e) { - // I don't know why this fails sometimes, but it does - print(e); + walletId: walletId, + txid: tx["hash"].toString(), + timestamp: int.parse(tx["local_timestamp"].toString()), + type: transactionType, + subType: TransactionSubType.none, + amount: 0, + amountString: amount.toJsonString(), + fee: 0, + height: int.parse(tx["height"].toString()), + isCancelled: false, + isLelantus: false, + slateId: "", + otherData: "", + inputs: [], + outputs: [], + nonce: 0, + ); + + Address address = Address( + walletId: walletId, + publicKey: [], + value: transactionType == TransactionType.incoming + ? publicAddress + : tx["account"].toString(), + derivationIndex: 0, + derivationPath: null, + type: transactionType == TransactionType.incoming + ? AddressType.nonWallet + : AddressType.nano, + subType: transactionType == TransactionType.incoming + ? AddressSubType.receiving + : AddressSubType.nonWallet, + ); + Tuple2 tuple = Tuple2(transaction, address); + transactionList.add(tuple); } + + await db.addNewTransactionData(transactionList, walletId); + return; } } @@ -523,8 +542,8 @@ class NanoWallet extends CoinServiceAPI Future fullRescan( int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async { await _prefs.init(); - await updateBalance(); await updateTransactions(); + await updateBalance(); } @override @@ -686,8 +705,36 @@ class NanoWallet extends CoinServiceAPI @override Future refresh() async { await _prefs.init(); - await updateBalance(); - await updateTransactions(); + + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.syncing, + walletId, + coin, + ), + ); + + try { + await updateChainHeight(); + await updateTransactions(); + await updateBalance(); + + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.synced, + walletId, + coin, + ), + ); + } catch (e) { + GlobalEventBus.instance.fire( + WalletSyncStatusChangedEvent( + WalletSyncStatus.unableToSync, + walletId, + coin, + ), + ); + } } @override @@ -741,4 +788,25 @@ class NanoWallet extends CoinServiceAPI bool validateAddress(String address) { return NanoAccounts.isValid(NanoAccountType.NANO, address); } + + Future updateChainHeight() async { + final String publicAddress = await getAddressFromMnemonic(); + // first get the account balance: + final infoBody = jsonEncode({ + "action": "account_info", + "account": publicAddress, + }); + final headers = { + "Content-Type": "application/json", + }; + final infoResponse = await http.post( + Uri.parse(getCurrentNode().host), + headers: headers, + body: infoBody, + ); + final infoData = jsonDecode(infoResponse.body); + + final int height = int.parse(infoData["confirmation_height"].toString()); + await updateCachedChainHeight(height); + } } diff --git a/lib/services/price.dart b/lib/services/price.dart index 16bd93c6a..a38fa24e8 100644 --- a/lib/services/price.dart +++ b/lib/services/price.dart @@ -90,7 +90,7 @@ class PriceAPI { Uri.parse("https://api.coingecko.com/api/v3/coins/markets?vs_currency" "=${baseCurrency.toLowerCase()}" "&ids=monero,bitcoin,litecoin,ecash,epic-cash,zcoin,dogecoin," - "bitcoin-cash,namecoin,wownero,ethereum,particl" + "bitcoin-cash,namecoin,wownero,ethereum,particl,nano" "&order=market_cap_desc&per_page=50&page=1&sparkline=false"); final coinGeckoResponse = await client.get( diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index a8b353c14..2ea630c13 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -33,7 +33,7 @@ abstract class Constants { BigInt.parse("1000000000000000000000000000000"); static final BigInt _satsPerCoin = BigInt.from(100000000); static const int _decimalPlaces = 8; - static const int _decimalPlacesNano = 6; + static const int _decimalPlacesNano = 30; static const int _decimalPlacesWownero = 11; static const int _decimalPlacesMonero = 12; static const int _decimalPlacesEthereum = 18;