Cw-343-a-list-of-previously-used-addresses (#1248)

* add used addresses list

* generate new address button

* fix wallet type issue

* fix addresses button title

* update selectButton

* show all wallet addresses

* add tx amount and balance

* fix ui

* remove cashAddr format

* fix generating new address issue

* disable autogenerating

* fix cashAddr format

* minor fix

* add search bar

* Update address_cell.dart

* fix merge conflict

* address labeling feature

* review fixes

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Serhii 2024-01-23 07:15:24 +02:00 committed by GitHub
parent d664f0a16f
commit 9754d67601
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 664 additions and 330 deletions

View file

@ -1,40 +1,70 @@
import 'dart:convert';
import 'package:bitbox/bitbox.dart' as bitbox;
class BitcoinAddressRecord {
BitcoinAddressRecord(this.address,
{required this.index, this.isHidden = false, bool isUsed = false})
: _isUsed = isUsed;
BitcoinAddressRecord(
this.address, {
required this.index,
this.isHidden = false,
int txCount = 0,
int balance = 0,
String name = '',
bool isUsed = false,
}) : _txCount = txCount,
_balance = balance,
_name = name,
_isUsed = isUsed;
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
final decoded = json.decode(jsonSource) as Map;
return BitcoinAddressRecord(
decoded['address'] as String,
index: decoded['index'] as int,
isHidden: decoded['isHidden'] as bool? ?? false,
isUsed: decoded['isUsed'] as bool? ?? false);
return BitcoinAddressRecord(decoded['address'] as String,
index: decoded['index'] as int,
isHidden: decoded['isHidden'] as bool? ?? false,
isUsed: decoded['isUsed'] as bool? ?? false,
txCount: decoded['txCount'] as int? ?? 0,
name: decoded['name'] as String? ?? '',
balance: decoded['balance'] as int? ?? 0);
}
@override
bool operator ==(Object o) =>
o is BitcoinAddressRecord && address == o.address;
final String address;
final bool isHidden;
final int index;
int _txCount;
int _balance;
String _name;
bool _isUsed;
int get txCount => _txCount;
String get name => _name;
int get balance => _balance;
set txCount(int value) => _txCount = value;
set balance(int value) => _balance = value;
bool get isUsed => _isUsed;
void setAsUsed() => _isUsed = true;
void setNewName(String label) => _name = label;
@override
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
@override
int get hashCode => address.hashCode;
bool _isUsed;
String get cashAddr => bitbox.Address.toCashAddress(address);
void setAsUsed() => _isUsed = true;
String toJSON() =>
json.encode({
String toJSON() => json.encode({
'address': address,
'index': index,
'isHidden': isHidden,
'isUsed': isUsed});
'txCount': txCount,
'name': name,
'isUsed': isUsed,
'balance': balance,
});
}

View file

@ -47,6 +47,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
.derivePath("m/0'/1"),
networkType: networkType);
autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});
}
static Future<BitcoinWallet> create({

View file

@ -63,6 +63,7 @@ abstract class ElectrumWalletBase
_password = password,
_feeRates = <int>[],
_isTransactionUpdating = false,
isEnabledAutoGenerateSubaddress = true,
unspentCoins = [],
_scripthashesUpdateSubject = {},
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
@ -87,6 +88,10 @@ abstract class ElectrumWalletBase
final bitcoin.HDWallet hd;
final String mnemonic;
@override
@observable
bool isEnabledAutoGenerateSubaddress;
late ElectrumClient electrumClient;
Box<UnspentCoinsInfo> unspentCoinsInfo;
@ -583,38 +588,66 @@ abstract class ElectrumWalletBase
Future<Map<String, ElectrumTransactionInfo>> fetchTransactions() async {
final addressHashes = <String, BitcoinAddressRecord>{};
final normalizedHistories = <Map<String, dynamic>>[];
final newTxCounts = <String, int>{};
walletAddresses.addresses.forEach((addressRecord) {
final sh = scriptHash(addressRecord.address, networkType: networkType);
addressHashes[sh] = addressRecord;
newTxCounts[sh] = 0;
});
final histories = addressHashes.keys.map((scriptHash) =>
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
final historyResults = await Future.wait(histories);
historyResults.forEach((history) {
history.entries.forEach((historyItem) {
if (historyItem.value.isNotEmpty) {
final address = addressHashes[historyItem.key];
address?.setAsUsed();
normalizedHistories.addAll(historyItem.value);
}
try {
final histories = addressHashes.keys.map((scriptHash) =>
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
final historyResults = await Future.wait(histories);
historyResults.forEach((history) {
history.entries.forEach((historyItem) {
if (historyItem.value.isNotEmpty) {
final address = addressHashes[historyItem.key];
address?.setAsUsed();
newTxCounts[historyItem.key] = historyItem.value.length;
normalizedHistories.addAll(historyItem.value);
}
});
});
});
final historiesWithDetails = await Future.wait(normalizedHistories.map((transaction) {
try {
return fetchTransactionInfo(
hash: transaction['tx_hash'] as String, height: transaction['height'] as int);
} catch (_) {
return Future.value(null);
for (var sh in addressHashes.keys) {
var balanceData = await electrumClient.getBalance(sh);
var addressRecord = addressHashes[sh];
if (addressRecord != null) {
addressRecord.balance = balanceData['confirmed'] as int? ?? 0;
}
}
}));
return historiesWithDetails
.fold<Map<String, ElectrumTransactionInfo>>(<String, ElectrumTransactionInfo>{}, (acc, tx) {
if (tx == null) {
addressHashes.forEach((sh, addressRecord) {
addressRecord.txCount = newTxCounts[sh] ?? 0;
});
final historiesWithDetails = await Future.wait(normalizedHistories.map((transaction) {
try {
return fetchTransactionInfo(
hash: transaction['tx_hash'] as String, height: transaction['height'] as int);
} catch (_) {
return Future.value(null);
}
}));
return historiesWithDetails.fold<Map<String, ElectrumTransactionInfo>>(
<String, ElectrumTransactionInfo>{}, (acc, tx) {
if (tx == null) {
return acc;
}
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
return acc;
}
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
return acc;
});
});
} catch (e) {
print(e.toString());
return {};
}
}
Future<void> updateTransactions() async {

View file

@ -1,5 +1,5 @@
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:bitbox/bitbox.dart' as bitbox;
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum.dart';
import 'package:cw_bitcoin/script_hash.dart';
@ -10,8 +10,7 @@ import 'package:mobx/mobx.dart';
part 'electrum_wallet_addresses.g.dart';
class ElectrumWalletAddresses = ElectrumWalletAddressesBase
with _$ElectrumWalletAddresses;
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
ElectrumWalletAddressesBase(WalletInfo walletInfo,
@ -22,19 +21,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
List<BitcoinAddressRecord>? initialAddresses,
int initialRegularAddressIndex = 0,
int initialChangeAddressIndex = 0})
: addresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? []).toSet()),
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? [])
: addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed)
.toSet()),
changeAddresses = ObservableList<BitcoinAddressRecord>.of(
(initialAddresses ?? [])
.toSet()),
changeAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed)
.toSet()),
.toSet()),
currentReceiveAddressIndex = initialRegularAddressIndex,
currentChangeAddressIndex = initialChangeAddressIndex,
super(walletInfo);
super(walletInfo);
static const defaultReceiveAddressesCount = 22;
static const defaultChangeAddressesCount = 17;
@ -42,6 +38,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
static String toCashAddr(String address) => bitbox.Address.toCashAddress(address);
static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address);
final ObservableList<BitcoinAddressRecord> addresses;
final ObservableList<BitcoinAddressRecord> receiveAddresses;
final ObservableList<BitcoinAddressRecord> changeAddresses;
@ -53,41 +51,67 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@override
@computed
String get address {
if (receiveAddresses.isEmpty) {
final address = generateNewAddress().address;
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(address) : address;
}
final receiveAddress = receiveAddresses.first.address;
if (isEnabledAutoGenerateSubaddress) {
if (receiveAddresses.isEmpty) {
final newAddress = generateNewAddress().address;
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
}
final receiveAddress = receiveAddresses.first.address;
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress;
return walletInfo.type == WalletType.bitcoinCash
? toCashAddr(receiveAddress)
: receiveAddress;
} else {
final receiveAddress = (receiveAddresses.first.address != addresses.first.address &&
previousAddressRecord != null)
? previousAddressRecord!.address
: addresses.first.address;
return walletInfo.type == WalletType.bitcoinCash
? toCashAddr(receiveAddress)
: receiveAddress;
}
}
@observable
bool isEnabledAutoGenerateSubaddress = true;
@override
set address(String addr) {
if (addr.startsWith('bitcoincash:')) {
addr = toLegacy(addr);
}
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == addr);
previousAddressRecord = addressRecord;
receiveAddresses.remove(addressRecord);
receiveAddresses.insert(0, addressRecord);
}
@override
String get primaryAddress => getAddress(index: 0, hd: mainHd);
@override
set address(String addr) => null;
int currentReceiveAddressIndex;
int currentChangeAddressIndex;
@computed
int get totalCountOfReceiveAddresses =>
addresses.fold(0, (acc, addressRecord) {
if (!addressRecord.isHidden) {
return acc + 1;
}
return acc;
});
@observable
BitcoinAddressRecord? previousAddressRecord;
@computed
int get totalCountOfChangeAddresses =>
addresses.fold(0, (acc, addressRecord) {
if (addressRecord.isHidden) {
return acc + 1;
}
return acc;
});
int get totalCountOfReceiveAddresses => addresses.fold(0, (acc, addressRecord) {
if (!addressRecord.isHidden) {
return acc + 1;
}
return acc;
});
@computed
int get totalCountOfChangeAddresses => addresses.fold(0, (acc, addressRecord) {
if (addressRecord.isHidden) {
return acc + 1;
}
return acc;
});
Future<void> discoverAddresses() async {
await _discoverAddresses(mainHd, false);
@ -117,11 +141,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (changeAddresses.isEmpty) {
final newAddresses = await _createNewAddresses(gap,
hd: sideHd,
startIndex: totalCountOfChangeAddresses > 0
? totalCountOfChangeAddresses - 1
: 0,
isHidden: true);
hd: sideHd,
startIndex: totalCountOfChangeAddresses > 0 ? totalCountOfChangeAddresses - 1 : 0,
isHidden: true);
_addAddresses(newAddresses);
}
@ -135,14 +157,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return address;
}
BitcoinAddressRecord generateNewAddress(
{bitcoin.HDWallet? hd, bool isHidden = false}) {
currentReceiveAddressIndex += 1;
// FIX-ME: Check logic for whichi HD should be used here ???
final address = BitcoinAddressRecord(
getAddress(index: currentReceiveAddressIndex, hd: hd ?? sideHd),
index: currentReceiveAddressIndex,
isHidden: isHidden);
BitcoinAddressRecord generateNewAddress({bitcoin.HDWallet? hd, String? label}) {
final isHidden = hd == sideHd;
final newAddressIndex = addresses.fold(
0, (int acc, addressRecord) => isHidden == addressRecord.isHidden ? acc + 1 : acc);
final address = BitcoinAddressRecord(getAddress(index: newAddressIndex, hd: hd ?? sideHd),
index: newAddressIndex, isHidden: isHidden, name: label ?? '');
addresses.add(address);
return address;
}
@ -160,20 +182,32 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
}
@action
void updateAddress(String address, String label) {
if (address.startsWith('bitcoincash:')) {
address = toLegacy(address);
}
final addressRecord = addresses.firstWhere((addressRecord) => addressRecord.address == address);
addressRecord.setNewName(label);
final index = addresses.indexOf(addressRecord);
addresses.remove(addressRecord);
addresses.insert(index, addressRecord);
}
@action
void updateReceiveAddresses() {
receiveAddresses.removeRange(0, receiveAddresses.length);
final newAdresses = addresses
.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
receiveAddresses.addAll(newAdresses);
final newAddresses =
addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
receiveAddresses.addAll(newAddresses);
}
@action
void updateChangeAddresses() {
changeAddresses.removeRange(0, changeAddresses.length);
final newAdresses = addresses
.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
changeAddresses.addAll(newAdresses);
final newAddresses =
addresses.where((addressRecord) => addressRecord.isHidden && !addressRecord.isUsed);
changeAddresses.addAll(newAddresses);
}
Future<void> _discoverAddresses(bitcoin.HDWallet hd, bool isHidden) async {
@ -181,20 +215,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
List<BitcoinAddressRecord> addrs;
if (addresses.isNotEmpty) {
addrs = addresses
.where((addr) => addr.isHidden == isHidden)
.toList();
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
} else {
addrs = await _createNewAddresses(
isHidden
? defaultChangeAddressesCount
: defaultReceiveAddressesCount,
isHidden ? defaultChangeAddressesCount : defaultReceiveAddressesCount,
startIndex: 0,
hd: hd,
isHidden: isHidden);
}
while(hasAddrUse) {
while (hasAddrUse) {
final addr = addrs.last.address;
hasAddrUse = await _hasAddressUsed(addr);
@ -204,11 +234,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final start = addrs.length;
final count = start + gap;
final batch = await _createNewAddresses(
count,
startIndex: start,
hd: hd,
isHidden: isHidden);
final batch = await _createNewAddresses(count, startIndex: start, hd: hd, isHidden: isHidden);
addrs.addAll(batch);
}
@ -232,21 +258,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
final newAddresses = await _createNewAddresses(
addressesCount,
startIndex: countOfReceiveAddresses,
hd: mainHd,
isHidden: false);
final newAddresses = await _createNewAddresses(addressesCount,
startIndex: countOfReceiveAddresses, hd: mainHd, isHidden: false);
addresses.addAll(newAddresses);
}
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
final newAddresses = await _createNewAddresses(
addressesCount,
startIndex: countOfHiddenAddresses,
hd: sideHd,
isHidden: true);
final newAddresses = await _createNewAddresses(addressesCount,
startIndex: countOfHiddenAddresses, hd: sideHd, isHidden: true);
addresses.addAll(newAddresses);
}
}
@ -256,10 +276,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final list = <BitcoinAddressRecord>[];
for (var i = startIndex; i < count + startIndex; i++) {
final address = BitcoinAddressRecord(
getAddress(index: i, hd: hd),
index: i,
isHidden: isHidden);
final address =
BitcoinAddressRecord(getAddress(index: i, hd: hd), index: i, isHidden: isHidden);
list.add(address);
}

View file

@ -51,6 +51,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
.fromSeed(seedBytes, network: networkType)
.derivePath("m/0'/1"),
networkType: networkType,);
autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});
}
static Future<LitecoinWallet> create({

View file

@ -57,6 +57,9 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
sideHd: bitcoin.HDWallet.fromSeed(seedBytes)
.derivePath("m/44'/145'/0'/1"),
networkType: networkType);
autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});
}

View file

@ -81,6 +81,7 @@ class AmountConverter {
return _moneroAmountToString(amount);
case CryptoCurrency.btc:
case CryptoCurrency.bch:
case CryptoCurrency.ltc:
return _bitcoinAmountToString(amount);
case CryptoCurrency.xhv:
case CryptoCurrency.xag:

View file

@ -63,9 +63,17 @@ class CWBitcoin extends Bitcoin {
}
@override
Future<void> generateNewAddress(Object wallet) async {
Future<void> generateNewAddress(Object wallet, String label) async {
final bitcoinWallet = wallet as ElectrumWallet;
await bitcoinWallet.walletAddresses.generateNewAddress();
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
await wallet.save();
}
@override
Future<void> updateAddress(Object wallet,String address, String label) async {
final bitcoinWallet = wallet as ElectrumWallet;
bitcoinWallet.walletAddresses.updateAddress(address, label);
await wallet.save();
}
@override
@ -99,6 +107,21 @@ class CWBitcoin extends Bitcoin {
.toList();
}
@override
@computed
List<ElectrumSubAddress> getSubAddresses(Object wallet) {
final electrumWallet = wallet as ElectrumWallet;
return electrumWallet.walletAddresses.addresses
.map((BitcoinAddressRecord addr) => ElectrumSubAddress(
id: addr.index,
name: addr.name,
address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address,
txCount: addr.txCount,
balance: addr.balance,
isChange: addr.isHidden))
.toList();
}
@override
String getAddress(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;

View file

@ -68,7 +68,8 @@ void startCurrentWalletChangeReaction(
.get<SharedPreferences>()
.setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type));
if (wallet.type == WalletType.monero) {
if (wallet.type == WalletType.monero || wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash ) {
_setAutoGenerateSubaddressStatus(wallet, settingsStore);
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@ -15,6 +16,7 @@ import 'package:cake_wallet/utils/share_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
@ -155,63 +157,27 @@ class AddressPage extends BasePage {
amountController: _amountController,
isLight: dashboardViewModel.settingsStore.currentTheme.type ==
ThemeType.light))),
SizedBox(height: 16),
Observer(builder: (_) {
if (addressListViewModel.hasAddressList) {
return GestureDetector(
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled
return SelectButton(
text: addressListViewModel.buttonTitle,
onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled &&
(WalletType.monero == addressListViewModel.wallet.type ||
WalletType.haven == addressListViewModel.wallet.type)
? await showPopUp<void>(
context: context, builder: (_) => getIt.get<MoneroAccountListPage>())
context: context,
builder: (_) => getIt.get<MoneroAccountListPage>())
: Navigator.of(context).pushNamed(Routes.receive),
child: Container(
height: 50,
padding: EdgeInsets.only(left: 24, right: 12),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(25)),
border: Border.all(
color:
Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
width: 1),
color: Theme.of(context)
.extension<SyncIndicatorTheme>()!
.syncedBackgroundColor),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Observer(
builder: (_) {
String label = addressListViewModel.hasAccounts
? S.of(context).accounts_subaddresses
: S.of(context).addresses;
if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) {
label = addressListViewModel.hasAccounts
? S.of(context).accounts
: S.of(context).account;
}
return Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.extension<SyncIndicatorTheme>()!
.textColor),
);
},
),
Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
)
],
),
),
textColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
borderColor: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
arrowColor: Theme.of(context).extension<SyncIndicatorTheme>()!.textColor,
textSize: 14,
height: 50,
);
} else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled ||
addressListViewModel.showElectrumAddressDisclaimer) {
addressListViewModel.isElectrumWallet) {
return Text(S.of(context).electrum_address_disclaimer,
textAlign: TextAlign.center,
style: TextStyle(

View file

@ -11,29 +11,37 @@ class SelectButton extends StatelessWidget {
this.isSelected = false,
this.showTrailingIcon = true,
this.height = 60,
this.textSize = 18,
this.color,
this.textColor,
this.arrowColor,
this.borderColor,
});
final Image? image;
final String text;
final double textSize;
final bool isSelected;
final VoidCallback onTap;
final bool showTrailingIcon;
final double height;
final Color? color;
final Color? textColor;
final Color? arrowColor;
final Color? borderColor;
@override
Widget build(BuildContext context) {
final color = isSelected
? Colors.green
: Theme.of(context).cardColor;
final textColor = isSelected
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
final effectiveTextColor = textColor ?? (isSelected
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor;
final arrowColor = isSelected
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
final effectiveArrowColor = arrowColor ?? (isSelected
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
: Theme.of(context).extension<FilterTheme>()!.titlesColor;
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
final selectArrowImage = Image.asset('assets/images/select_arrow.png',
color: arrowColor);
color: effectiveArrowColor);
return GestureDetector(
onTap: onTap,
@ -44,7 +52,9 @@ class SelectButton extends StatelessWidget {
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
color: color
color: backgroundColor,
border: borderColor != null ? Border.all(color: borderColor!) : null,
),
child: Row(
mainAxisSize: MainAxisSize.max,
@ -63,9 +73,9 @@ class SelectButton extends StatelessWidget {
child: Text(
text,
style: TextStyle(
fontSize: 18,
fontSize: textSize,
fontWeight: FontWeight.w500,
color: textColor
color: effectiveTextColor,
),
),
)

View file

@ -49,7 +49,7 @@ class ReceivePage extends BasePage {
bool get gradientBackground => true;
@override
bool get resizeToAvoidBottomInset => false;
bool get resizeToAvoidBottomInset => true;
final FocusNode _cryptoAmountFocus;
@ -99,10 +99,11 @@ class ReceivePage extends BasePage {
@override
Widget body(BuildContext context) {
final isElectrumWallet = addressListViewModel.isElectrumWallet;
return (addressListViewModel.type == WalletType.monero ||
addressListViewModel.type == WalletType.haven ||
addressListViewModel.type == WalletType.nano ||
addressListViewModel.type == WalletType.banano)
isElectrumWallet)
? KeyboardActions(
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
@ -140,7 +141,9 @@ class ReceivePage extends BasePage {
if (item is WalletAccountListHeader) {
cell = HeaderTile(
onTap: () async {
showTrailingButton: true,
walletAddressListViewModel: addressListViewModel,
trailingButtonTap: () async {
if (addressListViewModel.type == WalletType.monero ||
addressListViewModel.type == WalletType.haven) {
await showPopUp<void>(
@ -153,7 +156,7 @@ class ReceivePage extends BasePage {
}
},
title: S.of(context).accounts,
icon: Icon(
trailingIcon: Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
@ -161,16 +164,21 @@ class ReceivePage extends BasePage {
}
if (item is WalletAddressListHeader) {
cell = HeaderTile(
onTap: () =>
Navigator.of(context).pushNamed(Routes.newSubaddress),
title: S.of(context).addresses,
icon: Icon(
Icons.add,
size: 20,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor,
));
}
cell = HeaderTile(
title: S.of(context).addresses,
walletAddressListViewModel: addressListViewModel,
showTrailingButton: !addressListViewModel.isAutoGenerateSubaddressEnabled,
showSearchButton: true,
trailingButtonTap: () =>
Navigator.of(context).pushNamed(Routes.newSubaddress),
trailingIcon: Icon(
Icons.add,
size: 20,
color: Theme.of(context)
.extension<ReceivePageTheme>()!
.iconsColor,
));
}
if (item is WalletAddressListItem) {
cell = Observer(builder: (_) {
@ -185,6 +193,7 @@ class ReceivePage extends BasePage {
return AddressCell.fromItem(item,
isCurrent: isCurrent,
hasBalance: addressListViewModel.isElectrumWallet,
backgroundColor: backgroundColor,
textColor: textColor,
onTap: (_) => addressListViewModel.setAddress(item),

View file

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class AddressCell extends StatelessWidget {
AddressCell(
@ -12,13 +13,18 @@ class AddressCell extends StatelessWidget {
required this.backgroundColor,
required this.textColor,
this.onTap,
this.onEdit});
this.onEdit,
this.txCount,
this.balance,
this.isChange = false,
this.hasBalance = false});
factory AddressCell.fromItem(WalletAddressListItem item,
{required bool isCurrent,
required Color backgroundColor,
required Color textColor,
Function(String)? onTap,
bool hasBalance = false,
Function()? onEdit}) =>
AddressCell(
address: item.address,
@ -28,7 +34,11 @@ class AddressCell extends StatelessWidget {
backgroundColor: backgroundColor,
textColor: textColor,
onTap: onTap,
onEdit: onEdit);
onEdit: onEdit,
txCount: item.txCount,
balance: item.balance,
isChange: item.isChange,
hasBalance: hasBalance);
final String address;
final String name;
@ -38,17 +48,22 @@ class AddressCell extends StatelessWidget {
final Color textColor;
final Function(String)? onTap;
final Function()? onEdit;
final int? txCount;
final String? balance;
final bool isChange;
final bool hasBalance;
String get label {
if (name.isEmpty){
if(address.length<=16){
return address;
}else{
return address.substring(0,8)+'...'+
address.substring(address.length-8,address.length);
}
}else{
return name;
static const int addressPreviewLength = 8;
String get formattedAddress {
final formatIfCashAddr = address.replaceAll('bitcoincash:', '');
if (formatIfCashAddr.length <= (name.isNotEmpty ? 16 : 43)) {
return formatIfCashAddr;
} else {
return formatIfCashAddr.substring(0, addressPreviewLength) +
'...' +
formatIfCashAddr.substring(formatIfCashAddr.length - addressPreviewLength, formatIfCashAddr.length);
}
}
@ -59,41 +74,114 @@ class AddressCell extends StatelessWidget {
child: Container(
width: double.infinity,
color: backgroundColor,
padding: EdgeInsets.only(left: 24, right: 24, top: 28, bottom: 28),
child: Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: textColor,
),
padding: EdgeInsets.only(left: 24, right: 24, top: 20, bottom: 20),
child: Row(
children: [
Expanded(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
if (isChange)
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Container(
height: 20,
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.5)),
color: textColor),
alignment: Alignment.center,
child: Text(
S.of(context).unspent_change,
style: TextStyle(
color: backgroundColor,
fontSize: 10,
fontWeight: FontWeight.w600,
),
),
),
),
if (name.isNotEmpty)
Text(
'$name - ',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: textColor,
),
),
AutoSizeText(
formattedAddress,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: isChange ? 10 : 14,
color: textColor,
),
),
],
),
if (hasBalance)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Text(
'Balance: $balance',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: textColor,
),
),
Text(
'${S.of(context).transactions.toLowerCase()}: $txCount',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: textColor,
),
),
],
),
),
],
),
),
],
),
));
return Semantics(
label: S.of(context).slidable,
selected: isCurrent,
enabled: !isCurrent,
child: Slidable(
key: Key(address),
startActionPane: _actionPane(context),
endActionPane: _actionPane(context),
child: cell,
),
);
return onEdit == null
? cell
: Semantics(
label: S.of(context).slidable,
selected: isCurrent,
enabled: !isCurrent,
child: Slidable(
key: Key(address),
startActionPane: _actionPane(context),
endActionPane: _actionPane(context),
child: cell,
),
);
}
ActionPane _actionPane(BuildContext context) => ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
SlidableAction(
onPressed: (_) => onEdit?.call(),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
icon: Icons.edit,
label: S.of(context).edit,
),
],
);
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
SlidableAction(
onPressed: (_) => onEdit?.call(),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
icon: Icons.edit,
label: S.of(context).edit,
),
],
);
}

View file

@ -1,50 +1,114 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:flutter/material.dart';
class HeaderTile extends StatelessWidget {
class HeaderTile extends StatefulWidget {
HeaderTile({
required this.onTap,
required this.title,
required this.icon
required this.walletAddressListViewModel,
this.showSearchButton = false,
this.showTrailingButton = false,
this.trailingButtonTap,
this.trailingIcon,
});
final VoidCallback onTap;
final String title;
final Icon icon;
final WalletAddressListViewModel walletAddressListViewModel;
final bool showSearchButton;
final bool showTrailingButton;
final VoidCallback? trailingButtonTap;
final Icon? trailingIcon;
@override
_HeaderTileState createState() => _HeaderTileState();
}
class _HeaderTileState extends State<HeaderTile> {
bool _isSearchActive = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.only(
left: 24,
right: 24,
top: 24,
bottom: 24
),
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
),
Container(
height: 32,
width: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor),
child: icon,
)
],
),
final searchIcon = Image.asset("assets/images/search_icon.png",
color: Theme.of(context).extension<ReceivePageTheme>()!.iconsColor);
return Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
_isSearchActive
? Expanded(
child: TextField(
onChanged: (value) => widget.walletAddressListViewModel.updateSearchText(value),
cursorColor: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor,
cursorWidth: 0.5,
decoration: InputDecoration(
hintText: '${S.of(context).search}...',
isDense: true,
contentPadding: EdgeInsets.zero,
hintStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
border: UnderlineInputBorder(
borderSide: BorderSide(color: Theme.of(context).dividerColor),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Theme.of(context).dividerColor),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Theme.of(context).dividerColor),
),
),
autofocus: true,
),
)
: Text(
widget.title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor),
),
Row(
children: [
if (widget.showSearchButton)
GestureDetector(
onTap: () {
setState(() {
_isSearchActive = !_isSearchActive;
widget.walletAddressListViewModel.updateSearchText('');
});
},
child: Container(
height: 32,
width: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.extension<ReceivePageTheme>()!
.iconsBackgroundColor),
child: searchIcon,
)),
const SizedBox(width: 8),
if (widget.showTrailingButton)
GestureDetector(
onTap: widget.trailingButtonTap,
child: Container(
height: 32,
width: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
color:
Theme.of(context).extension<ReceivePageTheme>()!.iconsBackgroundColor),
child: widget.trailingIcon,
),
),
],
),
],
),
);
}

View file

@ -1,3 +1,4 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/entities/qr_view_data.dart';
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
import 'package:cake_wallet/routes.dart';
@ -6,6 +7,7 @@ import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dar
import 'package:cake_wallet/utils/brightness_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@ -144,9 +146,10 @@ class QRWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
child: AutoSizeText(
addressListViewModel.address.address,
textAlign: TextAlign.center,
maxLines: addressListViewModel.wallet.type == WalletType.monero ? 2 : 1,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,

View file

@ -38,7 +38,11 @@ abstract class PrivacySettingsViewModelBase with Store {
}
}
bool get isAutoGenerateSubaddressesVisible => _wallet.type == WalletType.monero;
bool get isAutoGenerateSubaddressesVisible =>
_wallet.type == WalletType.monero ||
_wallet.type == WalletType.bitcoin ||
_wallet.type == WalletType.litecoin ||
_wallet.type == WalletType.bitcoinCash;
@computed
bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress;

View file

@ -1,6 +1,5 @@
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/monero/monero.dart';
@ -33,7 +32,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
state = AddressEditOrCreateStateInitial(),
label = item?.name ?? '',
_item = item,
_wallet = wallet;
_wallet = wallet;
@observable
AddressEditOrCreateState state;
@ -46,6 +45,10 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
final WalletAddressListItem? _item;
final WalletBase _wallet;
bool get isElectrum => _wallet.type == WalletType.bitcoin ||
_wallet.type == WalletType.bitcoinCash ||
_wallet.type == WalletType.litecoin;
Future<void> save() async {
try {
state = AddressIsSaving();
@ -65,12 +68,7 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
Future<void> _createNew() async {
final wallet = _wallet;
if (wallet.type == WalletType.bitcoin
|| wallet.type == WalletType.litecoin
|| wallet.type == WalletType.bitcoinCash) {
await bitcoin!.generateNewAddress(wallet);
await wallet.save();
}
if (isElectrum) await bitcoin!.generateNewAddress(wallet, label);
if (wallet.type == WalletType.monero) {
await monero
@ -96,10 +94,8 @@ abstract class WalletAddressEditOrCreateViewModelBase with Store {
Future<void> _update() async {
final wallet = _wallet;
/*if (wallet is BitcoinWallet) {
await wallet.walletAddresses.updateAddress(_item.address as String);
await wallet.save();
}*/
if (isElectrum) await bitcoin!.updateAddress(wallet, _item!.address, label);
final index = _item?.id;
if (index != null) {
if (wallet.type == WalletType.monero) {

View file

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:cake_wallet/utils/list_item.dart';
class WalletAddressListItem extends ListItem {
@ -6,13 +5,19 @@ class WalletAddressListItem extends ListItem {
required this.address,
required this.isPrimary,
this.id,
this.name})
this.name,
this.txCount,
this.balance,
this.isChange = false})
: super();
final int? id;
final bool isPrimary;
final String address;
final String? name;
final int? txCount;
final String? balance;
final bool isChange;
@override
String toString() => name ?? address;

View file

@ -1,21 +1,25 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cw_core/currency.dart';
import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/utils/list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cw_core/amount_converter.dart';
import 'package:cw_core/currency.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart';
part 'wallet_address_list_view_model.g.dart';
@ -110,7 +114,8 @@ class EthereumURI extends PaymentURI {
class BitcoinCashURI extends PaymentURI {
BitcoinCashURI({required String amount, required String address})
: super(amount: amount, address: address);
: super(amount: amount, address: address);
@override
String toString() {
var base = address;
@ -121,9 +126,7 @@ class BitcoinCashURI extends PaymentURI {
return base;
}
}
}
class NanoURI extends PaymentURI {
NanoURI({required String amount, required String address})
@ -167,6 +170,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
hasAccounts =
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
amount = '',
_settingsStore = appStore.settingsStore,
super(appStore: appStore) {
_init();
}
@ -184,12 +188,28 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
final NumberFormat _cryptoNumberFormat;
final FiatConversionStore fiatConversionStore;
final SettingsStore _settingsStore;
List<Currency> get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
String get buttonTitle {
if (isElectrumWallet) {
return S.current.addresses;
}
if (isAutoGenerateSubaddressEnabled) {
return hasAccounts ? S.current.accounts : S.current.account;
}
return hasAccounts ? S.current.accounts_subaddresses : S.current.addresses;
}
@observable
Currency selectedCurrency;
@observable
String searchText = '';
@computed
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
@ -277,14 +297,21 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.addAll(addressItems);
}
if (wallet.type == WalletType.bitcoin) {
final primaryAddress = bitcoin!.getAddress(wallet);
final bitcoinAddresses = bitcoin!.getAddresses(wallet).map((addr) {
final isPrimary = addr == primaryAddress;
if (isElectrumWallet) {
final addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) {
final isPrimary = subaddress.id == 0;
return WalletAddressListItem(isPrimary: isPrimary, name: null, address: addr);
return WalletAddressListItem(
id: subaddress.id,
isPrimary: isPrimary,
name: subaddress.name,
address: subaddress.address,
txCount: subaddress.txCount,
balance: AmountConverter.amountIntToString(
walletTypeToCryptoCurrency(type), subaddress.balance),
isChange: subaddress.isChange);
});
addressList.addAll(bitcoinAddresses);
addressList.addAll(addressItems);
}
if (wallet.type == WalletType.ethereum) {
@ -299,6 +326,15 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
}
if (searchText.isNotEmpty) {
return ObservableList.of(addressList.where((item) {
if (item is WalletAddressListItem) {
return item.address.toLowerCase().contains(searchText.toLowerCase());
}
return false;
}));
}
return addressList;
}
@ -321,15 +357,23 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
@computed
bool get hasAddressList =>
wallet.type == WalletType.monero ||
wallet.type == WalletType.haven;/* ||
wallet.type == WalletType.nano ||
wallet.type == WalletType.banano;*/// TODO: nano accounts are disabled for now
wallet.type == WalletType.haven ||
wallet.type == WalletType.bitcoinCash ||
wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin;
// wallet.type == WalletType.nano ||
// wallet.type == WalletType.banano; TODO: nano accounts are disabled for now
@computed
bool get showElectrumAddressDisclaimer =>
bool get isElectrumWallet =>
wallet.type == WalletType.bitcoin ||
wallet.type == WalletType.litecoin ||
wallet.type == WalletType.bitcoinCash;
wallet.type == WalletType.litecoin ||
wallet.type == WalletType.bitcoinCash;
@computed
bool get isAutoGenerateSubaddressEnabled =>
_settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled;
List<ListItem> _baseItems;
@ -343,9 +387,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
_baseItems = [];
if (wallet.type == WalletType.monero ||
wallet.type == WalletType.haven /*||
wallet.type ==
WalletType
.haven /*||
wallet.type == WalletType.nano ||
wallet.type == WalletType.banano*/) {
wallet.type == WalletType.banano*/
) {
_baseItems.add(WalletAccountListHeader());
}
@ -367,6 +414,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
}
}
@action
void updateSearchText(String text) {
searchText = text;
}
void _convertAmountToCrypto() {
final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type);
try {

View file

@ -64,6 +64,7 @@ import 'package:cw_core/output_info.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';""";
const bitcoinCWHeaders = """
import 'package:cw_bitcoin/electrum_wallet.dart';
@ -76,9 +77,27 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_bitcoin/litecoin_wallet_service.dart';
import 'package:mobx/mobx.dart';
""";
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
const bitcoinContent = """
class ElectrumSubAddress {
ElectrumSubAddress({
required this.id,
required this.name,
required this.address,
required this.txCount,
required this.balance,
required this.isChange});
final int id;
final String name;
final String address;
final int txCount;
final int balance;
final bool isChange;
}
abstract class Bitcoin {
TransactionPriority getMediumTransactionPriority();
@ -92,13 +111,16 @@ abstract class Bitcoin {
TransactionPriority deserializeBitcoinTransactionPriority(int raw);
TransactionPriority deserializeLitecoinTransactionPriority(int raw);
int getFeeRate(Object wallet, TransactionPriority priority);
Future<void> generateNewAddress(Object wallet);
Future<void> generateNewAddress(Object wallet, String label);
Future<void> updateAddress(Object wallet,String address, String label);
Object createBitcoinTransactionCredentials(List<Output> outputs, {required TransactionPriority priority, int? feeRate});
Object createBitcoinTransactionCredentialsRaw(List<OutputInfo> outputs, {TransactionPriority? priority, required int feeRate});
List<String> getAddresses(Object wallet);
String getAddress(Object wallet);
List<ElectrumSubAddress> getSubAddresses(Object wallet);
String formatterBitcoinAmountToString({required int amount});
double formatterBitcoinAmountToDouble({required int amount});
int formatterStringDoubleToBitcoinAmount(String amount);