tx history working and fiat fixes

This commit is contained in:
fosse 2023-07-27 13:02:10 -04:00
parent 9ebf74fa01
commit d30de4dc3f
14 changed files with 492 additions and 119 deletions

View file

@ -120,7 +120,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const eos = CryptoCurrency(title: 'EOS', fullName: 'EOS', raw: 7, name: 'eos', iconPath: 'assets/images/eos_icon.png'); static const eos = CryptoCurrency(title: 'EOS', fullName: 'EOS', raw: 7, name: 'eos', iconPath: 'assets/images/eos_icon.png');
static const eth = CryptoCurrency(title: 'ETH', fullName: 'Ethereum', raw: 8, name: 'eth', iconPath: 'assets/images/eth_icon.png'); static const eth = CryptoCurrency(title: 'ETH', fullName: 'Ethereum', raw: 8, name: 'eth', iconPath: 'assets/images/eth_icon.png');
static const ltc = CryptoCurrency(title: 'LTC', fullName: 'Litecoin', raw: 9, name: 'ltc', iconPath: 'assets/images/litecoin-ltc_icon.png'); static const ltc = CryptoCurrency(title: 'LTC', fullName: 'Litecoin', raw: 9, name: 'ltc', iconPath: 'assets/images/litecoin-ltc_icon.png');
static const nano = CryptoCurrency(title: 'NANO', raw: 10, name: 'nano', iconPath: 'assets/images/nano.png'); static const nano = CryptoCurrency(title: 'XNO', raw: 10, name: 'nano', iconPath: 'assets/images/nano.png');
static const trx = CryptoCurrency(title: 'TRX', fullName: 'TRON', raw: 11, name: 'trx', iconPath: 'assets/images/trx_icon.png'); static const trx = CryptoCurrency(title: 'TRX', fullName: 'TRON', raw: 11, name: 'trx', iconPath: 'assets/images/trx_icon.png');
static const usdt = CryptoCurrency(title: 'USDT', tag: 'OMNI', fullName: 'USDT Tether', raw: 12, name: 'usdt', iconPath: 'assets/images/usdt_icon.png'); static const usdt = CryptoCurrency(title: 'USDT', tag: 'OMNI', fullName: 'USDT Tether', raw: 12, name: 'usdt', iconPath: 'assets/images/usdt_icon.png');
static const usdterc20 = CryptoCurrency(title: 'USDT', tag: 'ETH', fullName: 'USDT Tether', raw: 13, name: 'usdterc20', iconPath: 'assets/images/usdterc20_icon.png'); static const usdterc20 = CryptoCurrency(title: 'USDT', tag: 'ETH', fullName: 'USDT Tether', raw: 13, name: 'usdterc20', iconPath: 'assets/images/usdterc20_icon.png');

View file

@ -6,6 +6,10 @@ String rawToFormattedAmount(BigInt amount, Currency currency) {
return ""; return "";
} }
BigInt stringAmountToBigInt(String amount) {
return BigInt.zero;
}
class NanoBalance extends Balance { class NanoBalance extends Balance {
final BigInt currentBalance; final BigInt currentBalance;
final BigInt receivableBalance; final BigInt receivableBalance;
@ -17,16 +21,19 @@ class NanoBalance extends Balance {
this.formattedReceivableBalance = ""; this.formattedReceivableBalance = "";
} }
// NanoBalance.fromString( NanoBalance.fromString(
// {required this.formattedCurrentBalance, required this.formattedReceivableBalance}) {required this.formattedCurrentBalance, required this.formattedReceivableBalance})
// : currentBalance = moneroParseAmount(amount: formattedCurrentBalance), : currentBalance = stringAmountToBigInt(formattedCurrentBalance),
// receivableBalance = moneroParseAmount(amount: formattedReceivableBalance), receivableBalance = stringAmountToBigInt(formattedReceivableBalance),
// super(moneroParseAmount(amount: formattedReceivableBalance), super(0, 0);
// moneroParseAmount(amount: formattedCurrentBalance));
@override @override
String get formattedAvailableBalance => "0"; String get formattedAvailableBalance {
return "0";
@override }
String get formattedAdditionalBalance => "0";
@override
String get formattedAdditionalBalance {
return "0";
}
} }

View file

@ -0,0 +1,145 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/erc20_token.dart';
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_transaction_model.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'package:web3dart/web3dart.dart';
import 'package:web3dart/contracts/erc20.dart';
import 'package:cw_core/node.dart';
class NanoClient {
final _httpClient = Client();
StreamSubscription<Transfer>? subscription;
Node? _node;
bool connect(Node node) {
try {
_node = node;
return true;
} catch (e) {
return false;
}
}
void setListeners(EthereumAddress userAddress, Function(FilterEvent) onNewTransaction) async {}
Future<NanoBalance> getBalance(String address) async {
return NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero);
}
// Future<PendingEthereumTransaction> signTransaction({
// required EthPrivateKey privateKey,
// required String toAddress,
// required String amount,
// required int gas,
// required EthereumTransactionPriority priority,
// required CryptoCurrency currency,
// required int exponent,
// String? contractAddress,
// }) async {
// assert(currency == CryptoCurrency.eth || contractAddress != null);
// bool _isEthereum = currency == CryptoCurrency.eth;
// final price = await _client!.getGasPrice();
// final Transaction transaction = Transaction(
// from: privateKey.address,
// to: EthereumAddress.fromHex(toAddress),
// maxGas: gas,
// gasPrice: price,
// value: _isEthereum ? EtherAmount.inWei(BigInt.parse(amount)) : EtherAmount.zero(),
// );
// final signedTransaction = await _client!.signTransaction(privateKey, transaction);
// final BigInt estimatedGas;
// final Function _sendTransaction;
// if (_isEthereum) {
// estimatedGas = BigInt.from(21000);
// _sendTransaction = () async => await sendTransaction(signedTransaction);
// } else {
// estimatedGas = BigInt.from(50000);
// final erc20 = Erc20(
// client: _client!,
// address: EthereumAddress.fromHex(contractAddress!),
// );
// _sendTransaction = () async {
// await erc20.transfer(
// EthereumAddress.fromHex(toAddress),
// BigInt.parse(amount),
// credentials: privateKey,
// );
// };
// }
// return PendingEthereumTransaction(
// signedTransaction: signedTransaction,
// amount: amount,
// fee: estimatedGas * price.getInWei,
// sendTransaction: _sendTransaction,
// exponent: exponent,
// );
// }
Future<dynamic> getTransactionDetails(String transactionHash) async {
throw UnimplementedError();
}
void stop() {
subscription?.cancel();
_httpClient?.close();
}
Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
try {
final response = await _httpClient.post(_node!.uri,
headers: {"Content-Type": "application/json"},
body: jsonEncode({
"action": "account_history",
"account": address,
"count": "250",// TODO: pick a number
// "raw": true,
}));
final data = await jsonDecode(response.body);
final transactions = data["history"] is List ? data["history"] as List<dynamic> : [];
// Map the transactions list to NanoTransactionModel using the factory
// reversed so that the DateTime is correct when local_timestamp is absent
return transactions.reversed
.map<NanoTransactionModel>(
(transaction) => NanoTransactionModel.fromJson(transaction as Map<String, dynamic>))
.toList();
} catch (e) {
print(e);
return [];
}
}
// Future<int> _getDecimalPlacesForContract(DeployedContract contract) async {
// final String abi = await rootBundle.loadString("assets/abi_json/erc20_abi.json");
// final contractAbi = ContractAbi.fromJson(abi, "ERC20");
//
// final contract = DeployedContract(
// contractAbi,
// EthereumAddress.fromHex(_erc20Currencies[erc20Currency]!),
// );
// final decimalsFunction = contract.function('decimals');
// final decimals = await _client!.call(
// contract: contract,
// function: decimalsFunction,
// params: [],
// );
//
// int exponent = int.parse(decimals.first.toString());
// return exponent;
// }
}

View file

@ -1,28 +1,73 @@
import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_nano/file.dart';
import 'package:flutter/material.dart';
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_nano/nano_transaction_info.dart'; import 'package:cw_nano/nano_transaction_info.dart';
part 'nano_transaction_history.g.dart'; part 'nano_transaction_history.g.dart';
const transactionsHistoryFileName = 'transactions.json';
class NanoTransactionHistory = NanoTransactionHistoryBase class NanoTransactionHistory = NanoTransactionHistoryBase with _$NanoTransactionHistory;
with _$NanoTransactionHistory;
abstract class NanoTransactionHistoryBase abstract class NanoTransactionHistoryBase
extends TransactionHistoryBase<NanoTransactionInfo> with Store { extends TransactionHistoryBase<NanoTransactionInfo> with Store {
NanoTransactionHistoryBase() { NanoTransactionHistoryBase({required this.walletInfo, required String password})
: _password = password {
transactions = ObservableMap<String, NanoTransactionInfo>(); transactions = ObservableMap<String, NanoTransactionInfo>();
} }
@override final WalletInfo walletInfo;
Future<void> save() async {} String _password;
Future<void> init() async => await _load();
@override @override
void addOne(NanoTransactionInfo transaction) => Future<void> save() async {
transactions[transaction.id] = transaction; try {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final data = json.encode({'transactions': transactions});
await writeData(path: path, password: _password, data: data);
} catch (e) {
print('Error while save nano transaction history: ${e.toString()}');
}
}
@override
void addOne(NanoTransactionInfo transaction) => transactions[transaction.id] = transaction;
@override @override
void addMany(Map<String, NanoTransactionInfo> transactions) => void addMany(Map<String, NanoTransactionInfo> transactions) =>
this.transactions.addAll(transactions); this.transactions.addAll(transactions);
Future<Map<String, dynamic>> _read() async {
final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password);
return json.decode(content) as Map<String, dynamic>;
}
Future<void> _load() async {
try {
final content = await _read();
final txs = content['transactions'] as Map<String, dynamic>? ?? {};
txs.entries.forEach((entry) {
final val = entry.value;
if (val is Map<String, dynamic>) {
final tx = NanoTransactionInfo.fromJson(val);
_update(tx);
}
});
} catch (e) {
print(e);
}
}
void _update(NanoTransactionInfo transaction) => transactions[transaction.id] = transaction;
} }

View file

@ -1,72 +1,39 @@
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_monero/api/structs/transaction_info_row.dart';
import 'package:cw_core/parseBoolFromString.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/format_amount.dart'; import 'package:cw_core/format_amount.dart';
import 'package:cw_monero/api/transaction_history.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_nano/nano_util.dart';
class NanoTransactionInfo extends TransactionInfo { class NanoTransactionInfo extends TransactionInfo {
NanoTransactionInfo(this.id, this.height, this.direction, this.date, this.isPending, this.amount, NanoTransactionInfo({
this.accountIndex, this.addressIndex, this.amountRaw, this.confirmations); required this.id,
required this.height,
// NanoTransactionInfo.fromMap(Map<String, Object?> map) required this.amountRaw,
// : id = (map['hash'] ?? '') as String, this.tokenSymbol = "XNO",
// height = (map['height'] ?? 0) as int, required this.direction,
// direction = map['direction'] != null required this.confirmed,
// ? parseTransactionDirectionFromNumber(map['direction'] as String) required this.date,
// : TransactionDirection.incoming, required this.confirmations,
// date = DateTime.fromMillisecondsSinceEpoch( }) : this.amount = amountRaw.toInt();
// (int.tryParse(map['timestamp'] as String? ?? '') ?? 0) * 1000),
// isPending = parseBoolFromString(map['isPending'] as String),
// amount = map['amount'] as int,
// accountIndex = int.parse(map['accountIndex'] as String),
// addressIndex = map['addressIndex'] as int,
// confirmations = map['confirmations'] as int,
// key = getTxKey((map['hash'] ?? '') as String),
// fee = map['fee'] as int? ?? 0 {
// additionalInfo = <String, dynamic>{
// 'key': key,
// 'accountIndex': accountIndex,
// 'addressIndex': addressIndex
// };
// }
// NanoTransactionInfo.fromRow(TransactionInfoRow row)
// : id = row.getHash(),
// height = row.blockHeight,
// direction = parseTransactionDirectionFromInt(row.direction),
// date = DateTime.fromMillisecondsSinceEpoch(row.getDatetime() * 1000),
// isPending = row.isPending != 0,
// amount = row.getAmount(),
// accountIndex = row.subaddrAccount,
// addressIndex = row.subaddrIndex,
// confirmations = row.confirmations,
// key = getTxKey(row.getHash()),
// fee = row.fee {
// additionalInfo = <String, dynamic>{
// 'key': key,
// 'accountIndex': accountIndex,
// 'addressIndex': addressIndex
// };
// }
final String id; final String id;
final int height; final int height;
final TransactionDirection direction;
final DateTime date;
final int accountIndex;
final bool isPending;
final int amount; final int amount;
final BigInt amountRaw; final BigInt amountRaw;
final int addressIndex; final TransactionDirection direction;
final DateTime date;
final bool confirmed;
final int confirmations; final int confirmations;
String? recipientAddress; final String tokenSymbol;
String? key;
String? _fiatAmount; String? _fiatAmount;
bool get isPending => !this.confirmed;
@override @override
String amountFormatted() => '${formatAmount(moneroAmountToString(amount: amount))} XNO'; String amountFormatted() {
final String amt = NanoUtil.getRawAsUsableString(amountRaw.toString(), NanoUtil.rawPerNano);
final String acc = NanoUtil.getRawAccuracy(amountRaw.toString(), NanoUtil.rawPerNano);
return "$acc$amt $tokenSymbol";
}
@override @override
String fiatAmount() => _fiatAmount ?? ''; String fiatAmount() => _fiatAmount ?? '';
@ -75,5 +42,29 @@ class NanoTransactionInfo extends TransactionInfo {
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
@override @override
String feeFormatted() => '${formatAmount(moneroAmountToString(amount: 0))} XNO'; String feeFormatted() => "0 XNO";
factory NanoTransactionInfo.fromJson(Map<String, dynamic> data) {
return NanoTransactionInfo(
id: data['id'] as String,
height: data['height'] as int,
amountRaw: data['amountRaw'] as BigInt,
direction: parseTransactionDirectionFromInt(data['direction'] as int),
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
confirmed: data['confirmed'] as bool,
confirmations: data['confirmations'] as int,
tokenSymbol: data['tokenSymbol'] as String,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'height': height,
'amountRaw': amountRaw,
'direction': direction.index,
'date': date.millisecondsSinceEpoch,
'confirmed': confirmed,
'confirmations': confirmations,
'tokenSymbol': tokenSymbol,
};
} }

View file

@ -0,0 +1,39 @@
class NanoTransactionModel {
final DateTime? date;
final String hash;
final bool confirmed;
final String account;
final BigInt amount;
final int height;
final String type;
NanoTransactionModel({
this.date,
required this.hash,
required this.height,
required this.amount,
required this.confirmed,
required this.type,
required this.account,
});
factory NanoTransactionModel.fromJson(Map<String, dynamic> json) {
DateTime? local_timestamp;
try {
local_timestamp =
DateTime.fromMillisecondsSinceEpoch(int.parse(json["local_timeStamp"] as String) * 1000);
} catch (e) {
local_timestamp = DateTime.now();
}
return NanoTransactionModel(
date: local_timestamp,
hash: json["hash"] as String,
height: int.parse(json["height"] as String),
type: json["type"] as String,
amount: BigInt.parse(json["amount"] as String),
account: json["account"] as String,
confirmed: (json["confirmed"] as String) == "true",
);
}
}

View file

@ -8,6 +8,7 @@ import 'package:libcrypto/libcrypto.dart';
import 'package:nanodart/nanodart.dart'; import 'package:nanodart/nanodart.dart';
import 'package:ed25519_hd_key/ed25519_hd_key.dart'; import 'package:ed25519_hd_key/ed25519_hd_key.dart';
import 'package:nanodart/nanodart.dart'; import 'package:nanodart/nanodart.dart';
import 'package:decimal/decimal.dart';
class NanoUtil { class NanoUtil {
// standard: // standard:
@ -120,4 +121,80 @@ class NanoUtil {
// Ensure seed only contains hex characters, 0-9;A-F // Ensure seed only contains hex characters, 0-9;A-F
return NanoHelpers.isHexString(seed); return NanoHelpers.isHexString(seed);
} }
// number util:
static const int maxDecimalDigits = 6; // Max digits after decimal
static BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
static BigInt rawPerNyano = BigInt.parse("1000000000000000000000000");
static BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
static BigInt rawPerXMR = BigInt.parse("1000000000000");
static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
// static BigInt convertXMRtoNano = BigInt.parse("1000000000000000000000000000");
/// Convert raw to ban and return as BigDecimal
///
/// @param raw 100000000000000000000000000000
/// @return Decimal value 1.000000000000000000000000000000
///
static Decimal getRawAsDecimal(String? raw, BigInt? rawPerCur) {
rawPerCur ??= rawPerNano;
final Decimal amount = Decimal.parse(raw.toString());
final Decimal result = (amount / Decimal.parse(rawPerCur.toString())).toDecimal();
return result;
}
static String truncateDecimal(Decimal input, {int digits = maxDecimalDigits}) {
Decimal bigger = input.shift(digits);
bigger = bigger.floor(); // chop off the decimal: 1.059 -> 1.05
bigger = bigger.shift(-digits);
return bigger.toString();
}
/// Return raw as a NANO amount.
///
/// @param raw 100000000000000000000000000000
/// @returns 1
///
static String getRawAsUsableString(String? raw, BigInt rawPerCur) {
final String res =
truncateDecimal(getRawAsDecimal(raw, rawPerCur), digits: maxDecimalDigits + 9);
if (raw == null || raw == "0" || raw == "00000000000000000000000000000000") {
return "0";
}
if (!res.contains(".")) {
return res;
}
final String numAmount = res.split(".")[0];
String decAmount = res.split(".")[1];
// truncate:
if (decAmount.length > maxDecimalDigits) {
decAmount = decAmount.substring(0, maxDecimalDigits);
// remove trailing zeros:
decAmount = decAmount.replaceAllMapped(RegExp(r'0+$'), (Match match) => '');
if (decAmount.isEmpty) {
return numAmount;
}
}
return "$numAmount.$decAmount";
}
static String getRawAccuracy(String? raw, BigInt rawPerCur) {
final String rawString = getRawAsUsableString(raw, rawPerCur);
final String rawDecimalString = getRawAsDecimal(raw, rawPerCur).toString();
if (raw == null || raw.isEmpty || raw == "0") {
return "";
}
if (rawString != rawDecimalString) {
return "~";
}
return "";
}
} }

View file

@ -5,11 +5,13 @@ import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart'; import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_nano/file.dart'; import 'package:cw_nano/file.dart';
import 'package:cw_nano/nano_balance.dart'; import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_client.dart';
import 'package:cw_nano/nano_transaction_history.dart'; import 'package:cw_nano/nano_transaction_history.dart';
import 'package:cw_nano/nano_transaction_info.dart'; import 'package:cw_nano/nano_transaction_info.dart';
import 'package:cw_nano/nano_util.dart'; import 'package:cw_nano/nano_util.dart';
@ -40,7 +42,7 @@ abstract class NanoWalletBase
_mnemonic = mnemonic, _mnemonic = mnemonic,
_derivationType = derivationType, _derivationType = derivationType,
_isTransactionUpdating = false, _isTransactionUpdating = false,
_priorityFees = [], _client = NanoClient(),
walletAddresses = NanoWalletAddresses(walletInfo), walletAddresses = NanoWalletAddresses(walletInfo),
balance = ObservableMap<CryptoCurrency, NanoBalance>.of({ balance = ObservableMap<CryptoCurrency, NanoBalance>.of({
CryptoCurrency.nano: initialBalance ?? CryptoCurrency.nano: initialBalance ??
@ -48,7 +50,7 @@ abstract class NanoWalletBase
}), }),
super(walletInfo) { super(walletInfo) {
this.walletInfo = walletInfo; this.walletInfo = walletInfo;
transactionHistory = NanoTransactionHistory(); transactionHistory = NanoTransactionHistory(walletInfo: walletInfo, password: password);
} }
final String _mnemonic; final String _mnemonic;
@ -59,8 +61,7 @@ abstract class NanoWalletBase
late final String _publicAddress; late final String _publicAddress;
late final String _seedKey; late final String _seedKey;
List<int> _priorityFees; late NanoClient _client;
int? _gasPrice;
bool _isTransactionUpdating; bool _isTransactionUpdating;
@override @override
@ -84,7 +85,7 @@ abstract class NanoWalletBase
this.walletInfo.address = _publicAddress; this.walletInfo.address = _publicAddress;
await walletAddresses.init(); await walletAddresses.init();
// await transactionHistory.init(); await transactionHistory.init();
// walletAddresses.address = _privateKey.address.toString(); // walletAddresses.address = _privateKey.address.toString();
await save(); await save();
@ -103,14 +104,24 @@ abstract class NanoWalletBase
@override @override
void close() { void close() {
// _client.stop(); _client.stop();
} }
@action @action
@override @override
Future<void> connectToNode({required Node node}) async { Future<void> connectToNode({required Node node}) async {
print("f"); try {
throw UnimplementedError(); syncStatus = ConnectingSyncStatus();
final isConnected = _client.connect(node);
if (!isConnected) {
throw Exception("Ethereum Node connection failed");
}
// _client.setListeners(_privateKey.address, _onNewTransaction);
_updateBalance();
syncStatus = ConnectedSyncStatus();
} catch (e) {
syncStatus = FailedSyncStatus();
}
} }
@override @override
@ -120,14 +131,45 @@ abstract class NanoWalletBase
} }
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
print("h"); print("updating_transactions");
throw UnimplementedError(); try {
if (_isTransactionUpdating) {
return;
}
_isTransactionUpdating = true;
final transactions = await fetchTransactions();
transactionHistory.addMany(transactions);
await transactionHistory.save();
_isTransactionUpdating = false;
} catch (_) {
_isTransactionUpdating = false;
}
} }
@override @override
Future<Map<String, NanoTransactionInfo>> fetchTransactions() async { Future<Map<String, NanoTransactionInfo>> fetchTransactions() async {
print("i"); String address = _publicAddress;
throw UnimplementedError();
final transactions = await _client.fetchTransactions(address);
final Map<String, NanoTransactionInfo> result = {};
for (var transactionModel in transactions) {
result[transactionModel.hash] = NanoTransactionInfo(
id: transactionModel.hash,
amountRaw: transactionModel.amount,
height: transactionModel.height,
direction: transactionModel.account == address
? TransactionDirection.outgoing
: TransactionDirection.incoming,
confirmed: transactionModel.confirmed,
date: transactionModel.date ?? DateTime.now(),
confirmations: transactionModel.confirmed ? 1 : 0,
);
}
return result;
} }
@override @override
@ -156,11 +198,15 @@ abstract class NanoWalletBase
@action @action
@override @override
Future<void> startSync() async { Future<void> startSync() async {
throw UnimplementedError(); try {
} syncStatus = AttemptingSyncStatus();
await _updateBalance();
await updateTransactions();
int feeRate(TransactionPriority priority) { syncStatus = SyncedSyncStatus();
throw UnimplementedError(); } catch (e) {
syncStatus = FailedSyncStatus();
}
} }
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type); Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
@ -177,16 +223,16 @@ abstract class NanoWalletBase
required String password, required String password,
required WalletInfo walletInfo, required WalletInfo walletInfo,
}) async { }) async {
// TODO: finish
final path = await pathForWallet(name: name, type: walletInfo.type); final path = await pathForWallet(name: name, type: walletInfo.type);
final jsonSource = await read(path: path, password: password); final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String; final mnemonic = data['mnemonic'] as String;
final balance = /*NanoBalance.fromJSON(data['balance'] as String) ?? */ final balance = NanoBalance.fromString(
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero); formattedCurrentBalance: data['balance'] as String? ?? "0",
formattedReceivableBalance: "0");
DerivationType derivationType = DerivationType.bip39; DerivationType derivationType = DerivationType.bip39;
if (data['derivationType'] == "nano") { if (data['derivationType'] == "DerivationType.nano") {
derivationType = DerivationType.nano; derivationType = DerivationType.nano;
} }
@ -203,11 +249,6 @@ abstract class NanoWalletBase
await save(); await save();
} }
Future<EthPrivateKey> getPrivateKey(String mnemonic, String password) async {
print("o");
throw UnimplementedError();
}
Future<void>? updateBalance() async => await _updateBalance(); Future<void>? updateBalance() async => await _updateBalance();
void _onNewTransaction(FilterEvent event) { void _onNewTransaction(FilterEvent event) {

View file

@ -208,6 +208,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.4" version: "2.2.4"
decimal:
dependency: "direct main"
description:
name: decimal
sha256: "24a261d5d5c87e86c7651c417a5dbdf8bcd7080dd592533910e8d0505a279f21"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
ed25519_hd_key: ed25519_hd_key:
dependency: "direct main" dependency: "direct main"
description: description:
@ -586,6 +594,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.3" version: "1.2.3"
rational:
dependency: transitive
description:
name: rational
sha256: ba58e9e18df9abde280e8b10051e4bce85091e41e8e7e411b6cde2e738d357cf
url: "https://pub.dev"
source: hosted
version: "2.2.2"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:

View file

@ -17,6 +17,7 @@ dependencies:
bip39: ^1.0.6 bip39: ^1.0.6
bip32: ^2.0.0 bip32: ^2.0.0
nanodart: ^2.0.0 nanodart: ^2.0.0
decimal: ^2.3.3
libcrypto: ^0.2.2 libcrypto: ^0.2.2
ed25519_hd_key: ^2.2.0 ed25519_hd_key: ^2.2.0
hex: ^0.2.0 hex: ^0.2.0

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/nano/nano.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -17,8 +18,11 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
return haven!.getTransactionPriorities(); return haven!.getTransactionPriorities();
case WalletType.ethereum: case WalletType.ethereum:
return ethereum!.getTransactionPriorities(); return ethereum!.getTransactionPriorities();
// we just get ethereum's here since there's no transaction priority in nano
// and so there's no point in bothering to implement it:
case WalletType.nano:
return ethereum!.getTransactionPriorities();
default: default:
return []; return [];
} }
} }

View file

@ -31,8 +31,8 @@ class MenuWidgetState extends State<MenuWidget> {
this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'), this.litecoinIcon = Image.asset('assets/images/litecoin_menu.png'),
this.havenIcon = Image.asset('assets/images/haven_menu.png'), this.havenIcon = Image.asset('assets/images/haven_menu.png'),
this.ethereumIcon = Image.asset('assets/images/eth_icon.png'), this.ethereumIcon = Image.asset('assets/images/eth_icon.png'),
this.nanoIcon = Image.asset('assets/images/eth_icon.png'), this.nanoIcon = Image.asset('assets/images/nano_icon.png'),
this.bananoIcon = Image.asset('assets/images/eth_icon.png'); this.bananoIcon = Image.asset('assets/images/nano_icon.png');
final largeScreen = 731; final largeScreen = 731;

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/nano/nano.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:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
@ -13,12 +14,12 @@ import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cw_core/keyable.dart'; import 'package:cw_core/keyable.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cw_nano/nano_transaction_info.dart';
import 'package:cw_nano/nano_util.dart';
class TransactionListItem extends ActionListItem with Keyable { class TransactionListItem extends ActionListItem with Keyable {
TransactionListItem( TransactionListItem(
{required this.transaction, {required this.transaction, required this.balanceViewModel, required this.settingsStore});
required this.balanceViewModel,
required this.settingsStore});
final TransactionInfo transaction; final TransactionInfo transaction;
final BalanceViewModel balanceViewModel; final BalanceViewModel balanceViewModel;
@ -34,10 +35,9 @@ class TransactionListItem extends ActionListItem with Keyable {
dynamic get keyIndex => transaction.id; dynamic get keyIndex => transaction.id;
String get formattedCryptoAmount { String get formattedCryptoAmount {
return displayMode == BalanceDisplayMode.hiddenBalance return displayMode == BalanceDisplayMode.hiddenBalance ? '---' : transaction.amountFormatted();
? '---'
: transaction.amountFormatted();
} }
String get formattedTitle { String get formattedTitle {
if (transaction.direction == TransactionDirection.incoming) { if (transaction.direction == TransactionDirection.incoming) {
return S.current.received; return S.current.received;
@ -92,6 +92,14 @@ class TransactionListItem extends ActionListItem with Keyable {
cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction), cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction),
price: price); price: price);
break; break;
case WalletType.nano:
final nanoTransaction = transaction as NanoTransactionInfo;
amount = calculateFiatAmountRaw(
cryptoAmount:
NanoUtil.getRawAsDecimal(nanoTransaction.amountRaw.toString(), NanoUtil.rawPerNano)
.toDouble(),
price: price);
break;
default: default:
break; break;
} }

View file

@ -116,7 +116,6 @@ class NanoURI extends PaymentURI {
@override @override
String toString() { String toString() {
print(address);
var base = 'nano:' + address; var base = 'nano:' + address;
if (amount.isNotEmpty) { if (amount.isNotEmpty) {