Cw 498 improve wallet accounts (#1152)

* CW-498 Call updateTransactions on account change

* CW-498 Code Cleanup

* CW-498 Add Wallet Address to trade to match transactions to trades

* CW-498 Fix containsAddress to include only addresses of account

* Handle nullability for fromWalletAddress

---------

Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
Konstantin Ullrich 2023-11-02 16:52:47 +01:00 committed by GitHub
parent 5bd382f186
commit e5d99313eb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 206 additions and 230 deletions

View file

@ -36,4 +36,6 @@ abstract class WalletAddresses {
print(e.toString());
}
}
bool containsAddress(String address) => addressesMap.containsKey(address);
}

View file

@ -63,14 +63,14 @@ abstract class HavenWalletAddressesBase extends WalletAddressesWithAccount<Accou
bool validate() {
accountList.update();
final accountListLength = accountList.accounts.length ?? 0;
final accountListLength = accountList.accounts.length;
if (accountListLength <= 0) {
return false;
}
subaddressList.update(accountIndex: accountList.accounts.first.id);
final subaddressListLength = subaddressList.subaddresses.length ?? 0;
final subaddressListLength = subaddressList.subaddresses.length;
if (subaddressListLength <= 0) {
return false;
@ -84,4 +84,8 @@ abstract class HavenWalletAddressesBase extends WalletAddressesWithAccount<Accou
subaddress = subaddressList.subaddresses.first;
address = subaddress!.address;
}
@override
bool containsAddress(String address) =>
addressInfos[account?.id ?? 0]?.any((it) => it.address == address) ?? false;
}

View file

@ -57,9 +57,7 @@ abstract class MoneroWalletBase
walletAddresses = MoneroWalletAddresses(walletInfo, transactionHistory);
_onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) {
if (account == null) {
return;
}
if (account == null) return;
balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{
currency: MoneroBalance(
@ -67,6 +65,7 @@ abstract class MoneroWalletBase
unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: account.id))
});
_updateSubAddress(isEnabledAutoGenerateSubaddress, account: account);
_askForUpdateTransactionHistory();
});
reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) {
@ -362,9 +361,7 @@ abstract class MoneroWalletBase
}
@override
Future<void> changePassword(String password) async {
monero_wallet.setPasswordSync(password);
}
Future<void> changePassword(String password) async => monero_wallet.setPasswordSync(password);
Future<int> getNodeHeight() async => monero_wallet.getNodeHeight();
@ -499,9 +496,8 @@ abstract class MoneroWalletBase
}
}
String getSubaddressLabel(int accountIndex, int addressIndex) {
return monero_wallet.getSubaddressLabel(accountIndex, addressIndex);
}
String getSubaddressLabel(int accountIndex, int addressIndex) =>
monero_wallet.getSubaddressLabel(accountIndex, addressIndex);
List<MoneroTransactionInfo> _getAllTransactionsOfAccount(int? accountIndex) => transaction_history
.getAllTransactions()

View file

@ -1,11 +1,11 @@
import 'package:cw_core/account.dart';
import 'package:cw_core/address_info.dart';
import 'package:cw_core/subaddress.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/account.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/monero_account_list.dart';
import 'package:cw_monero/monero_subaddress_list.dart';
import 'package:cw_core/subaddress.dart';
import 'package:cw_monero/monero_transaction_history.dart';
import 'package:mobx/mobx.dart';
@ -112,4 +112,8 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
subaddress = subaddressList.subaddresses.last;
address = subaddress!.address;
}
@override
bool containsAddress(String address) =>
addressInfos[account?.id ?? 0]?.any((it) => it.address == address) ?? false;
}

View file

@ -27,6 +27,7 @@ class Trade extends HiveObject {
this.password,
this.providerId,
this.providerName,
this.fromWalletAddress
}) {
if (provider != null) providerRaw = provider.raw;
@ -101,6 +102,9 @@ class Trade extends HiveObject {
@HiveField(16)
String? providerName;
@HiveField(17)
String? fromWalletAddress;
static Trade fromMap(Map<String, Object?> map) {
return Trade(
id: map['id'] as String,
@ -110,7 +114,9 @@ class Trade extends HiveObject {
createdAt:
map['date'] != null ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int) : null,
amount: map['amount'] as String,
walletId: map['wallet_id'] as String);
walletId: map['wallet_id'] as String,
fromWalletAddress: map['from_wallet_address'] as String?
);
}
Map<String, dynamic> toMap() {
@ -121,7 +127,8 @@ class Trade extends HiveObject {
'output': to.serialize(),
'date': createdAt != null ? createdAt!.millisecondsSinceEpoch : null,
'amount': amount,
'wallet_id': walletId
'wallet_id': walletId,
'from_wallet_address': fromWalletAddress
};
}

View file

@ -120,8 +120,6 @@ class CWMoneroWalletDetails extends MoneroWalletDetails {
@computed
@override
MoneroBalance get balance {
final moneroWallet = _wallet as MoneroWallet;
final balance = moneroWallet.balance;
throw Exception('Unimplemented');
// return MoneroBalance();
//return MoneroBalance(
@ -132,14 +130,10 @@ class CWMoneroWalletDetails extends MoneroWalletDetails {
class CWMonero extends Monero {
@override
MoneroAccountList getAccountList(Object wallet) {
return CWMoneroAccountList(wallet);
}
MoneroAccountList getAccountList(Object wallet) => CWMoneroAccountList(wallet);
@override
MoneroSubaddressList getSubaddressList(Object wallet) {
return CWMoneroSubaddressList(wallet);
}
MoneroSubaddressList getSubaddressList(Object wallet) => CWMoneroSubaddressList(wallet);
@override
TransactionHistoryBase getTransactionHistory(Object wallet) {
@ -148,19 +142,13 @@ class CWMonero extends Monero {
}
@override
MoneroWalletDetails getMoneroWalletDetails(Object wallet) {
return CWMoneroWalletDetails(wallet);
}
MoneroWalletDetails getMoneroWalletDetails(Object wallet) => CWMoneroWalletDetails(wallet);
@override
int getHeigthByDate({required DateTime date}) {
return getMoneroHeigthByDate(date: date);
}
int getHeightByDate({required DateTime date}) => getMoneroHeigthByDate(date: date);
@override
TransactionPriority getDefaultTransactionPriority() {
return MoneroTransactionPriority.automatic;
}
TransactionPriority getDefaultTransactionPriority() => MoneroTransactionPriority.automatic;
@override
TransactionPriority getMoneroTransactionPrioritySlow() => MoneroTransactionPriority.slow;
@ -170,14 +158,11 @@ class CWMonero extends Monero {
MoneroTransactionPriority.automatic;
@override
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
return MoneroTransactionPriority.deserialize(raw: raw);
}
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) =>
MoneroTransactionPriority.deserialize(raw: raw);
@override
List<TransactionPriority> getTransactionPriorities() {
return MoneroTransactionPriority.all;
}
List<TransactionPriority> getTransactionPriorities() => MoneroTransactionPriority.all;
@override
List<String> getMoneroWordList(String language) {
@ -215,8 +200,8 @@ class CWMonero extends Monero {
required String address,
required String password,
required String language,
required int height}) {
return MoneroRestoreWalletFromKeysCredentials(
required int height}) =>
MoneroRestoreWalletFromKeysCredentials(
name: name,
spendKey: spendKey,
viewKey: viewKey,
@ -224,26 +209,22 @@ class CWMonero extends Monero {
password: password,
language: language,
height: height);
}
@override
WalletCredentials createMoneroRestoreWalletFromSeedCredentials(
{required String name,
required String password,
required int height,
required String mnemonic}) {
return MoneroRestoreWalletFromSeedCredentials(
required String mnemonic}) =>
MoneroRestoreWalletFromSeedCredentials(
name: name, password: password, height: height, mnemonic: mnemonic);
}
@override
WalletCredentials createMoneroNewWalletCredentials({
required String name,
required String language,
String? password,
}) {
return MoneroNewWalletCredentials(name: name, password: password, language: language);
}
String? password}) =>
MoneroNewWalletCredentials(name: name, password: password, language: language);
@override
Map<String, String> getKeys(Object wallet) {
@ -259,8 +240,8 @@ class CWMonero extends Monero {
@override
Object createMoneroTransactionCreationCredentials(
{required List<Output> outputs, required TransactionPriority priority}) {
return MoneroTransactionCreationCredentials(
{required List<Output> outputs, required TransactionPriority priority}) =>
MoneroTransactionCreationCredentials(
outputs: outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
@ -273,29 +254,23 @@ class CWMonero extends Monero {
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as MoneroTransactionPriority);
}
@override
Object createMoneroTransactionCreationCredentialsRaw(
{required List<OutputInfo> outputs, required TransactionPriority priority}) {
return MoneroTransactionCreationCredentials(
{required List<OutputInfo> outputs, required TransactionPriority priority}) =>
MoneroTransactionCreationCredentials(
outputs: outputs, priority: priority as MoneroTransactionPriority);
}
@override
String formatterMoneroAmountToString({required int amount}) {
return moneroAmountToString(amount: amount);
}
String formatterMoneroAmountToString({required int amount}) =>
moneroAmountToString(amount: amount);
@override
double formatterMoneroAmountToDouble({required int amount}) {
return moneroAmountToDouble(amount: amount);
}
double formatterMoneroAmountToDouble({required int amount}) =>
moneroAmountToDouble(amount: amount);
@override
int formatterMoneroParseAmount({required String amount}) {
return moneroParseAmount(amount: amount);
}
int formatterMoneroParseAmount({required String amount}) => moneroParseAmount(amount: amount);
@override
Account getCurrentAccount(Object wallet) {
@ -312,9 +287,7 @@ class CWMonero extends Monero {
}
@override
void onStartup() {
monero_wallet_api.onStartup();
}
void onStartup() => monero_wallet_api.onStartup();
@override
int getTransactionInfoAccountId(TransactionInfo tx) {
@ -324,9 +297,8 @@ class CWMonero extends Monero {
@override
WalletService createMoneroWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
return MoneroWalletService(walletInfoSource, unspentCoinSource);
}
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) =>
MoneroWalletService(walletInfoSource, unspentCoinSource);
@override
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {

View file

@ -36,16 +36,13 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
restoreHeightController.addListener(() {
if (restoreHeightController.text.isNotEmpty) {
widget.onHeightOrDateEntered?.call(true);
}
else {
} else {
widget.onHeightOrDateEntered?.call(false);
dateController.text = '';
}
try {
_changeHeight(restoreHeightController.text != null &&
restoreHeightController.text.isNotEmpty
? int.parse(restoreHeightController.text)
: 0);
_changeHeight(
restoreHeightController.text.isNotEmpty ? int.parse(restoreHeightController.text) : 0);
} catch (_) {
_changeHeight(0);
}
@ -117,7 +114,7 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
);
}
Future _selectDate(BuildContext context) async {
Future<void> _selectDate(BuildContext context) async {
final now = DateTime.now();
final date = await getDate(
context: context,
@ -126,7 +123,7 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
lastDate: now);
if (date != null) {
final height = monero!.getHeigthByDate(date: date);
final height = monero!.getHeightByDate(date: date);
setState(() {
dateController.text = DateFormat('yyyy-MM-dd').format(date);
restoreHeightController.text = '$height';

View file

@ -1,7 +1,7 @@
import 'package:cw_core/wallet_base.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:mobx/mobx.dart';
part'trade_filter_store.g.dart';
@ -87,29 +87,31 @@ abstract class TradeFilterStoreBase with Store {
}
List<TradeListItem> filtered({required List<TradeListItem> trades, required WalletBase wallet}) {
final _trades =
trades.where((item) => item.trade.walletId == wallet.id).toList();
final _trades = trades
.where((item) => item.trade.walletId == wallet.id && isTradeInAccount(item, wallet))
.toList();
final needToFilter = !displayAllTrades;
return needToFilter
? _trades
.where((item) =>
(displayXMRTO &&
item.trade.provider == ExchangeProviderDescription.xmrto) ||
(displayXMRTO && item.trade.provider == ExchangeProviderDescription.xmrto) ||
(displaySideShift &&
item.trade.provider == ExchangeProviderDescription.sideShift) ||
(displayChangeNow &&
item.trade.provider ==
ExchangeProviderDescription.changeNow) ||
item.trade.provider == ExchangeProviderDescription.changeNow) ||
(displayMorphToken &&
item.trade.provider ==
ExchangeProviderDescription.morphToken)
||(displaySimpleSwap &&
item.trade.provider ==
ExchangeProviderDescription.simpleSwap)
||(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador)
||(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix))
item.trade.provider == ExchangeProviderDescription.morphToken) ||
(displaySimpleSwap &&
item.trade.provider == ExchangeProviderDescription.simpleSwap) ||
(displayTrocador && item.trade.provider == ExchangeProviderDescription.trocador) ||
(displayExolix && item.trade.provider == ExchangeProviderDescription.exolix))
.toList()
: _trades;
}
bool isTradeInAccount(TradeListItem item, WalletBase wallet) =>
item.trade.fromWalletAddress == null
? true
: wallet.walletAddresses.containsAddress(item.trade.fromWalletAddress!);
}

View file

@ -1,36 +1,36 @@
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/buy_provider_types.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/balance.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/dashboard/orders_store.dart';
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
import 'package:cake_wallet/view_model/dashboard/formatted_item_list.dart';
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/view_model/dashboard/formatted_item_list.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:mobx/mobx.dart';
part 'dashboard_view_model.g.dart';
@ -76,33 +76,33 @@ abstract class DashboardViewModelBase with Store {
FilterItem(
value: () => tradeFilterStore.displayAllTrades,
caption: S.current.all_trades,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.all)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.all)),
FilterItem(
value: () => tradeFilterStore.displayChangeNow,
caption: ExchangeProviderDescription.changeNow.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
FilterItem(
value: () => tradeFilterStore.displaySideShift,
caption: ExchangeProviderDescription.sideShift.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
FilterItem(
value: () => tradeFilterStore.displaySimpleSwap,
caption: ExchangeProviderDescription.simpleSwap.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
FilterItem(
value: () => tradeFilterStore.displayTrocador,
caption: ExchangeProviderDescription.trocador.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.trocador)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.trocador)),
FilterItem(
value: () => tradeFilterStore.displayExolix,
caption: ExchangeProviderDescription.exolix.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.exolix)),
onChanged: () =>
tradeFilterStore.toggleDisplayExchange(ExchangeProviderDescription.exolix)),
]
},
subname = '',
@ -222,9 +222,8 @@ abstract class DashboardViewModelBase with Store {
BalanceDisplayMode get balanceDisplayMode => appStore.settingsStore.balanceDisplayMode;
@computed
bool get shouldShowMarketPlaceInDashboard {
return appStore.settingsStore.shouldShowMarketPlaceInDashboard;
}
bool get shouldShowMarketPlaceInDashboard =>
appStore.settingsStore.shouldShowMarketPlaceInDashboard;
@computed
List<TradeListItem> get trades =>

View file

@ -1,25 +1,18 @@
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
class TradeListItem extends ActionListItem {
TradeListItem({
required this.trade,
required this.settingsStore});
TradeListItem({required this.trade, required this.settingsStore});
final Trade trade;
final SettingsStore settingsStore;
BalanceDisplayMode get displayMode => settingsStore.balanceDisplayMode;
String get tradeFormattedAmount {
return trade.amount != null
? displayMode == BalanceDisplayMode.hiddenBalance
? '---'
: trade.amountFormatted()
: trade.amount;
}
String get tradeFormattedAmount =>
displayMode == BalanceDisplayMode.hiddenBalance ? '---' : trade.amountFormatted();
@override
DateTime get date => trade.createdAt!;

View file

@ -491,6 +491,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
final trade =
await provider.createTrade(request: request, isFixedRateMode: isFixedRateMode);
trade.walletId = wallet.id;
trade.fromWalletAddress = wallet.walletAddresses.address;
tradesStore.setTrade(trade);
await trades.add(trade);
tradeState = TradeIsCreatedSuccessfully(trade: trade);

View file

@ -1,4 +1,3 @@
import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/transaction_info.dart';
@ -194,7 +193,7 @@ abstract class WalletKeysViewModelBase with Store {
int _getRestoreHeightByTransactions(WalletType type, DateTime date) {
if (type == WalletType.monero) {
return monero!.getHeigthByDate(date: date);
return monero!.getHeightByDate(date: date);
} else if (type == WalletType.haven) {
return haven!.getHeightByDate(date: date);
}

View file

@ -235,7 +235,7 @@ abstract class Monero {
String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex);
int getHeigthByDate({required DateTime date});
int getHeightByDate({required DateTime date});
TransactionPriority getDefaultTransactionPriority();
TransactionPriority getMoneroTransactionPrioritySlow();
TransactionPriority getMoneroTransactionPriorityAutomatic();