Skip to content

Commit

Permalink
CW-611-Refactor-Address-Handling (#1630)
Browse files Browse the repository at this point in the history
* subaddress fix

* fix subaddress generation

* rewrite usedAddresses for xmr and wow

* [skip ci] remove print statements

* refactor address handling

* do not remove manual addresses, just mark them

* monero display latest address on receive page when autogenerate is enabled [skip ci]

* WIP subaddresses, hidden addresses, and UI improvements for monero

* update configure script

* fix subaddress generation, display latest address

* Update lib/core/wallet_loading_service.dart

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* Exclude manually created addresses

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* don't call .save function multiple times

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* - revert usedAddress functinality
- add mutex to prevent crashes
- fix UI flashing in tx screen
- fixes from comments

* account index fixes
added code to wownero
code comment

* - added subaddress index
- fixed received count also accounting for change (we don't want that)
- fix bad state: no element
- fix search
- fix automatic generation

* prevent crashes by acquiring mutex before setting the pointer

* - fix ttDetails generation in larger/restored wallets
- show manual add icon in monero/wownero even when autogeneration is enabled
- disable colors on non-debug builds
- cache getAddress call in xmr/wow
[skip ci]

* fix: silent payment error in address setter
enable fancy new features only for xmr / wow

* refresh subaddress list, when we add new address
fix manual addresses marking

* add toggle to hide and show address

* update transaction details after restore

* show only one address in address book for xmr, wow and haven

* fix address book
reset address only when autogenerate is on

* enable isEnabledAutoGenerateSubaddress on new wallets

* hide addresses after exchange only for XMR and WOW

* fix: bad-state no element

* Update cw_monero/lib/monero_wallet_addresses.dart

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* Update cw_monero/lib/monero_wallet_addresses.dart

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

* improvements to performance

* 0, 0 -> accountIndex, addressIndex

* make constant variables final

* Update cw_wownero/lib/wownero_wallet_addresses.dart [skip ci]

* Update cw_wownero/lib/wownero_wallet_addresses.dart [skip ci]

* Update cw_monero/lib/monero_wallet.dart [skip ci]

* fix potential exception

* fix after removing late

* remove orElse, replaced it with a try catch block.
fix strings

* fix valid seed function

* fix null check error [skip ci]

* fix updateSubaddressList for wow and haven

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
  • Loading branch information
MrCyjaneK and OmarHatem28 authored Sep 28, 2024
1 parent 62e0c2a commit f8b0c0a
Show file tree
Hide file tree
Showing 35 changed files with 776 additions and 243 deletions.
3 changes: 2 additions & 1 deletion cw_bitcoin/lib/electrum_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1772,7 +1772,8 @@ abstract class ElectrumWalletBase
final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type);
final hiddenAddresses = addressesByType.where((addr) => addr.isHidden == true);
final receiveAddresses = addressesByType.where((addr) => addr.isHidden == false);

walletAddresses.hiddenAddresses.addAll(hiddenAddresses.map((e) => e.address));
await walletAddresses.saveAddressesInBox();
await Future.wait(addressesByType.map((addressRecord) async {
final history = await _fetchAddressHistory(addressRecord, await getCurrentChainTip());

Expand Down
12 changes: 10 additions & 2 deletions cw_bitcoin/lib/electrum_wallet_addresses.dart
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {

@override
set address(String addr) {
if (addr == "Silent Payments" && SilentPaymentsAddresType.p2sp != addressPageType) {
return;
}
if (addressPageType == SilentPaymentsAddresType.p2sp) {
final selected = silentAddresses.firstWhere((addressRecord) => addressRecord.address == addr);

Expand All @@ -174,12 +177,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
return;
}

final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == addr);
try {
final addressRecord = _addresses.firstWhere(
(addressRecord) => addressRecord.address == addr,
);

previousAddressRecord = addressRecord;
receiveAddresses.remove(addressRecord);
receiveAddresses.insert(0, addressRecord);
} catch (e) {
print("ElectrumWalletAddressBase: set address ($addr): $e");
}
}

@override
Expand Down
14 changes: 12 additions & 2 deletions cw_core/lib/subaddress.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
class Subaddress {
Subaddress({required this.id, required this.address, required this.label});
Subaddress({
required this.id,
required this.address,
required this.label,
this.balance = null,
this.txCount = null,
});

Subaddress.fromMap(Map<String, Object?> map)
: this.id = map['id'] == null ? 0 : int.parse(map['id'] as String),
this.address = (map['address'] ?? '') as String,
this.label = (map['label'] ?? '') as String;
this.label = (map['label'] ?? '') as String,
this.balance = (map['balance'] ?? '') as String?,
this.txCount = (map['txCount'] ?? '') as int?;

final int id;
final String address;
final String label;
final String? balance;
final int? txCount;
}
40 changes: 37 additions & 3 deletions cw_core/lib/wallet_addresses.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,58 @@
import 'package:cw_core/address_info.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';

abstract class WalletAddresses {
WalletAddresses(this.walletInfo)
: addressesMap = {},
allAddressesMap = {},
addressInfos = {};
addressInfos = {},
usedAddresses = {},
hiddenAddresses = walletInfo.hiddenAddresses?.toSet() ?? {},
manualAddresses = walletInfo.manualAddresses?.toSet() ?? {};

final WalletInfo walletInfo;

String get address;

String get latestAddress {
if (walletInfo.type == WalletType.monero || walletInfo.type == WalletType.wownero) {
if (addressesMap.keys.length == 0) return address;
return addressesMap[addressesMap.keys.last] ?? address;
}
return _localAddress ?? address;
}

String? get primaryAddress => null;

set address(String address);
String? _localAddress;

set address(String address) => _localAddress = address;

String get addressForExchange => address;

Map<String, String> addressesMap;
Map<String, String> allAddressesMap;

Map<String, String> get usableAddressesMap {
final tmp = addressesMap.map((key, value) => MapEntry(key, value)); // copy address map
tmp.removeWhere((key, value) => hiddenAddresses.contains(key) || manualAddresses.contains(key));
return tmp;
}

Map<String, String> get usableAllAddressesMap {
final tmp = allAddressesMap.map((key, value) => MapEntry(key, value)); // copy address map
tmp.removeWhere((key, value) => hiddenAddresses.contains(key) || manualAddresses.contains(key));
return tmp;
}

Map<int, List<AddressInfo>> addressInfos;

Set<String> usedAddresses = {};
Set<String> usedAddresses;

Set<String> hiddenAddresses;

Set<String> manualAddresses;

Future<void> init();

Expand All @@ -32,6 +64,8 @@ abstract class WalletAddresses {
walletInfo.addresses = addressesMap;
walletInfo.addressInfos = addressInfos;
walletInfo.usedAddresses = usedAddresses.toList();
walletInfo.hiddenAddresses = hiddenAddresses.toList();
walletInfo.manualAddresses = manualAddresses.toList();

if (walletInfo.isInBox) {
await walletInfo.save();
Expand Down
9 changes: 9 additions & 0 deletions cw_core/lib/wallet_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ class WalletInfo extends HiveObject {

@HiveField(22)
String? parentAddress;

@HiveField(23)
List<String>? hiddenAddresses;

@HiveField(24)
List<String>? manualAddresses;




String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';

Expand Down
8 changes: 5 additions & 3 deletions cw_haven/lib/haven_wallet_addresses.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:cw_core/wallet_addresses_with_account.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/account.dart';
import 'package:cw_haven/api/wallet.dart';
import 'package:cw_haven/haven_account_list.dart';
import 'package:cw_haven/haven_subaddress_list.dart';
import 'package:cw_core/subaddress.dart';
Expand Down Expand Up @@ -36,7 +37,7 @@ abstract class HavenWalletAddressesBase extends WalletAddressesWithAccount<Accou
@override
Future<void> init() async {
accountList.update();
account = accountList.accounts.first;
account = accountList.accounts.isEmpty ? Account(id: 0, label: "Primary address") : accountList.accounts.first;
updateSubaddressList(accountIndex: account?.id ?? 0);
await updateAddressesInBox();
}
Expand Down Expand Up @@ -81,8 +82,9 @@ abstract class HavenWalletAddressesBase extends WalletAddressesWithAccount<Accou

void updateSubaddressList({required int accountIndex}) {
subaddressList.update(accountIndex: accountIndex);
subaddress = subaddressList.subaddresses.first;
address = subaddress!.address;
address = subaddressList.subaddresses.isNotEmpty
? subaddressList.subaddresses.first.address
: getAddress();
}

@override
Expand Down
82 changes: 75 additions & 7 deletions cw_monero/lib/api/subaddress_list.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/transaction_history.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:monero/monero.dart' as monero;

Expand All @@ -14,6 +15,10 @@ class SubaddressInfoMetadata {

SubaddressInfoMetadata? subaddress = null;

String getRawLabel({required int accountIndex, required int addressIndex}) {
return monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
}

void refreshSubaddresses({required int accountIndex}) {
try {
isUpdating = true;
Expand All @@ -29,31 +34,94 @@ class Subaddress {
Subaddress({
required this.addressIndex,
required this.accountIndex,
required this.received,
required this.txCount,
});
String get address => monero.Wallet_address(
wptr!,
accountIndex: accountIndex,
addressIndex: addressIndex,
);
late String address = getAddress(
accountIndex: accountIndex,
addressIndex: addressIndex,
);
final int addressIndex;
final int accountIndex;
String get label => monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
final int received;
final int txCount;
String get label {
final localLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
if (localLabel.startsWith("#$addressIndex")) return localLabel; // don't duplicate the ID if it was user-providen
return "#$addressIndex ${localLabel}".trim();
}
}

class TinyTransactionDetails {
TinyTransactionDetails({
required this.address,
required this.amount,
});
final List<String> address;
final int amount;
}

int lastWptr = 0;
int lastTxCount = 0;
List<TinyTransactionDetails> ttDetails = [];

List<Subaddress> getAllSubaddresses() {
txhistory = monero.Wallet_history(wptr!);
final txCount = monero.TransactionHistory_count(txhistory!);
if (lastTxCount != txCount && lastWptr != wptr!.address) {
final List<TinyTransactionDetails> newttDetails = [];
lastTxCount = txCount;
lastWptr = wptr!.address;
for (var i = 0; i < txCount; i++) {
final tx = monero.TransactionHistory_transaction(txhistory!, index: i);
if (monero.TransactionInfo_direction(tx) == monero.TransactionInfo_Direction.Out) continue;
final subaddrs = monero.TransactionInfo_subaddrIndex(tx).split(",");
final account = monero.TransactionInfo_subaddrAccount(tx);
newttDetails.add(TinyTransactionDetails(
address: List.generate(subaddrs.length, (index) => getAddress(accountIndex: account, addressIndex: int.tryParse(subaddrs[index])??0)),
amount: monero.TransactionInfo_amount(tx),
));
}
ttDetails.clear();
ttDetails.addAll(newttDetails);
}
final size = monero.Wallet_numSubaddresses(wptr!, accountIndex: subaddress!.accountIndex);
final list = List.generate(size, (index) {
final ttDetailsLocal = ttDetails.where((element) {
final address = getAddress(
accountIndex: subaddress!.accountIndex,
addressIndex: index,
);
if (element.address.contains(address)) return true;
return false;
}).toList();
int received = 0;
for (var i = 0; i < ttDetailsLocal.length; i++) {
received += ttDetailsLocal[i].amount;
}
return Subaddress(
accountIndex: subaddress!.accountIndex,
addressIndex: index,
received: received,
txCount: ttDetailsLocal.length,
);
}).reversed.toList();
if (list.length == 0) {
list.add(Subaddress(addressIndex: subaddress!.accountIndex, accountIndex: 0));
list.add(
Subaddress(
addressIndex: subaddress!.accountIndex,
accountIndex: 0,
received: 0,
txCount: 0,
));
}
return list;
}

int numSubaddresses(int subaccountIndex) {
return monero.Wallet_numSubaddresses(wptr!, accountIndex: subaccountIndex);
}

void addSubaddressSync({required int accountIndex, required String label}) {
monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex, label: label);
refreshSubaddresses(accountIndex: accountIndex);
Expand Down
Loading

0 comments on commit f8b0c0a

Please sign in to comment.