CW-415 add coin control for xmr (#985)

* Added CoinsInfo to monero_api_cpp

* Add struct on dart

* Add struct on dart

* Set coins value

* CW-415 Use add-coin-to-monero branch

* CW-415 Add get Coin and build Monero Deps using Docker

* CW-415 Fix Typo

* CW-415 add debug log info

* CW-415 Add preferred key Images for coin control to Monero

* CW-415 Fix generation

* CW-415 Skip GA Cache Externals

* CW-415 Skip GA Cache Externals

* CW-415 Coin Control: remove Block Explorer for Monero, Add Tx hash, save note on field exit

* CW-415 Coin Control: Throw Exception when all outputs are deselected

* CW-415 Coin Control: Show Frozen Balance on Dashboard

* CW-415 Coin Control: Show Frozen Balance on Dashboard

* CW-415 Ignore cached Monero deps in Workflow

* CW-415 Fix displaying frozen Balance

* Use own Translator with http 1.1.0

* CW-415 Resolve requested Changes

* CW-415 Resolve requested Changes

* CW-415 Resolve requested Changes

* CW-415 Apply requested Changes

* CW-415 Apply requested Changes

* CW-415 Ensure opening of UnspentCoinsInfo Box, even for Monero.com

---------

Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com>
This commit is contained in:
Godwin Asuquo
2023-08-24 16:54:05 +03:00
committed by GitHub
co-authored by Konstantin Ullrich
parent 3577730de8
commit f6670c0236
56 changed files with 1584 additions and 545 deletions
+3 -2
View File
@@ -128,7 +128,8 @@ class CWBitcoin extends Bitcoin {
bitcoinUnspent.address.address,
bitcoinUnspent.hash,
bitcoinUnspent.value,
bitcoinUnspent.vout))
bitcoinUnspent.vout,
null))
.toList();
}
@@ -160,4 +161,4 @@ class CWBitcoin extends Bitcoin {
@override
TransactionPriority getLitecoinTransactionPrioritySlow()
=> LitecoinTransactionPriority.slow;
}
}
+6 -6
View File
@@ -216,10 +216,10 @@ late Box<Template> _templates;
late Box<ExchangeTemplate> _exchangeTemplates;
late Box<TransactionDescription> _transactionDescriptionBox;
late Box<Order> _ordersSource;
late Box<UnspentCoinsInfo>? _unspentCoinsInfoSource;
late Box<UnspentCoinsInfo> _unspentCoinsInfoSource;
late Box<AnonpayInvoiceInfo> _anonpayInvoiceInfoSource;
Future setup({
Future<void> setup({
required Box<WalletInfo> walletInfoSource,
required Box<Node> nodeSource,
required Box<Contact> contactSource,
@@ -228,7 +228,7 @@ Future setup({
required Box<ExchangeTemplate> exchangeTemplates,
required Box<TransactionDescription> transactionDescriptionBox,
required Box<Order> ordersSource,
Box<UnspentCoinsInfo>? unspentCoinsInfoSource,
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfoSource,
}) async {
_walletInfoSource = walletInfoSource;
@@ -731,11 +731,11 @@ Future setup({
case WalletType.haven:
return haven!.createHavenWalletService(_walletInfoSource);
case WalletType.monero:
return monero!.createMoneroWalletService(_walletInfoSource);
return monero!.createMoneroWalletService(_walletInfoSource, _unspentCoinsInfoSource);
case WalletType.bitcoin:
return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource!);
return bitcoin!.createBitcoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
case WalletType.litecoin:
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource!);
return bitcoin!.createLitecoinWalletService(_walletInfoSource, _unspentCoinsInfoSource);
case WalletType.ethereum:
return ethereum!.createEthereumWalletService(_walletInfoSource);
default:
@@ -0,0 +1,18 @@
class Unspent {
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
: isSending = true,
isFrozen = false,
note = '';
final String address;
final String hash;
final int value;
final int vout;
final String? keyImage;
bool isSending;
bool isFrozen;
String note;
bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc');
}
+2 -6
View File
@@ -133,11 +133,7 @@ Future<void> initializeAppConfigs() async {
final templates = await CakeHive.openBox<Template>(Template.boxName);
final exchangeTemplates = await CakeHive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final anonpayInvoiceInfo = await CakeHive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName);
Box<UnspentCoinsInfo>? unspentCoinsInfoSource;
if (!isMoneroOnly) {
unspentCoinsInfoSource = await CakeHive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
}
final unspentCoinsInfoSource = await CakeHive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
await initialSetup(
sharedPreferences: await SharedPreferences.getInstance(),
@@ -169,7 +165,7 @@ Future<void> initialSetup(
required Box<TransactionDescription> transactionDescriptions,
required FlutterSecureStorage secureStorage,
required Box<AnonpayInvoiceInfo> anonpayInvoiceInfo,
Box<UnspentCoinsInfo>? unspentCoinsInfoSource,
required Box<UnspentCoinsInfo> unspentCoinsInfoSource,
int initialMigrationVersion = 15}) async {
LanguageService.loadLocaleList();
await defaultSettingsMigration(
+272 -270
View File
@@ -1,361 +1,363 @@
part of 'monero.dart';
class CWMoneroAccountList extends MoneroAccountList {
CWMoneroAccountList(this._wallet);
final Object _wallet;
CWMoneroAccountList(this._wallet);
@override
@computed
final Object _wallet;
@override
@computed
ObservableList<Account> get accounts {
final moneroWallet = _wallet as MoneroWallet;
final accounts = moneroWallet.walletAddresses.accountList
.accounts
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
.toList();
return ObservableList<Account>.of(accounts);
final moneroWallet = _wallet as MoneroWallet;
final accounts = moneroWallet.walletAddresses.accountList.accounts
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
.toList();
return ObservableList<Account>.of(accounts);
}
@override
void update(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.accountList.update();
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.accountList.update();
}
@override
void refresh(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.accountList.refresh();
}
void refresh(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.accountList.refresh();
}
@override
@override
List<Account> getAll(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.walletAddresses.accountList
.getAll()
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
.toList();
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.walletAddresses.accountList
.getAll()
.map((acc) => Account(id: acc.id, label: acc.label, balance: acc.balance))
.toList();
}
@override
Future<void> addAccount(Object wallet, {required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList.addAccount(label: label);
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList.addAccount(label: label);
}
@override
Future<void> setLabelAccount(Object wallet, {required int accountIndex, required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList
.setLabelAccount(
accountIndex: accountIndex,
label: label);
Future<void> setLabelAccount(Object wallet,
{required int accountIndex, required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList
.setLabelAccount(accountIndex: accountIndex, label: label);
}
}
class CWMoneroSubaddressList extends MoneroSubaddressList {
CWMoneroSubaddressList(this._wallet);
final Object _wallet;
CWMoneroSubaddressList(this._wallet);
@override
@computed
final Object _wallet;
@override
@computed
ObservableList<Subaddress> get subaddresses {
final moneroWallet = _wallet as MoneroWallet;
final subAddresses = moneroWallet.walletAddresses.subaddressList
.subaddresses
.map((sub) => Subaddress(
id: sub.id,
address: sub.address,
label: sub.label))
.toList();
return ObservableList<Subaddress>.of(subAddresses);
final moneroWallet = _wallet as MoneroWallet;
final subAddresses = moneroWallet.walletAddresses.subaddressList.subaddresses
.map((sub) => Subaddress(id: sub.id, address: sub.address, label: sub.label))
.toList();
return ObservableList<Subaddress>.of(subAddresses);
}
@override
void update(Object wallet, {required int accountIndex}) {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
}
@override
void refresh(Object wallet, {required int accountIndex}) {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex);
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.subaddressList.refresh(accountIndex: accountIndex);
}
@override
List<Subaddress> getAll(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.walletAddresses
.subaddressList
.getAll()
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
.toList();
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.walletAddresses.subaddressList
.getAll()
.map((sub) => Subaddress(id: sub.id, label: sub.label, address: sub.address))
.toList();
}
@override
Future<void> addSubaddress(Object wallet, {required int accountIndex, required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.subaddressList
.addSubaddress(
accountIndex: accountIndex,
label: label);
Future<void> addSubaddress(Object wallet,
{required int accountIndex, required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.subaddressList
.addSubaddress(accountIndex: accountIndex, label: label);
}
@override
Future<void> setLabelSubaddress(Object wallet,
{required int accountIndex, required int addressIndex, required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.subaddressList
.setLabelSubaddress(
accountIndex: accountIndex,
addressIndex: addressIndex,
label: label);
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.subaddressList
.setLabelSubaddress(accountIndex: accountIndex, addressIndex: addressIndex, label: label);
}
}
class CWMoneroWalletDetails extends MoneroWalletDetails {
CWMoneroWalletDetails(this._wallet);
final Object _wallet;
CWMoneroWalletDetails(this._wallet);
@computed
final Object _wallet;
@computed
@override
Account get account {
final moneroWallet = _wallet as MoneroWallet;
final acc = moneroWallet.walletAddresses.account;
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
final moneroWallet = _wallet as MoneroWallet;
final acc = moneroWallet.walletAddresses.account;
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
}
@computed
@override
MoneroBalance get balance {
final moneroWallet = _wallet as MoneroWallet;
final balance = moneroWallet.balance;
MoneroBalance get balance {
final moneroWallet = _wallet as MoneroWallet;
final balance = moneroWallet.balance;
throw Exception('Unimplemented');
// return MoneroBalance();
//return MoneroBalance(
// fullBalance: balance.fullBalance,
// unlockedBalance: balance.unlockedBalance);
}
// return MoneroBalance();
//return MoneroBalance(
// fullBalance: balance.fullBalance,
// unlockedBalance: balance.unlockedBalance);
}
}
class CWMonero extends Monero {
@override
MoneroAccountList getAccountList(Object wallet) {
return CWMoneroAccountList(wallet);
}
@override
MoneroSubaddressList getSubaddressList(Object wallet) {
return CWMoneroSubaddressList(wallet);
}
@override
TransactionHistoryBase getTransactionHistory(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.transactionHistory;
}
@override
MoneroWalletDetails getMoneroWalletDetails(Object wallet) {
return CWMoneroWalletDetails(wallet);
}
@override
int getHeigthByDate({required DateTime date}) {
return getMoneroHeigthByDate(date: date);
}
@override
TransactionPriority getDefaultTransactionPriority() {
return MoneroTransactionPriority.automatic;
}
MoneroAccountList getAccountList(Object wallet) {
return CWMoneroAccountList(wallet);
}
@override
TransactionPriority getMoneroTransactionPrioritySlow()
=> MoneroTransactionPriority.slow;
MoneroSubaddressList getSubaddressList(Object wallet) {
return CWMoneroSubaddressList(wallet);
}
@override
TransactionPriority getMoneroTransactionPriorityAutomatic()
=> MoneroTransactionPriority.automatic;
TransactionHistoryBase getTransactionHistory(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.transactionHistory;
}
@override
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
return MoneroTransactionPriority.deserialize(raw: raw);
}
@override
MoneroWalletDetails getMoneroWalletDetails(Object wallet) {
return CWMoneroWalletDetails(wallet);
}
@override
List<TransactionPriority> getTransactionPriorities() {
return MoneroTransactionPriority.all;
}
@override
int getHeigthByDate({required DateTime date}) {
return getMoneroHeigthByDate(date: date);
}
@override
List<String> getMoneroWordList(String language) {
switch (language.toLowerCase()) {
case 'english':
return EnglishMnemonics.words;
case 'chinese (simplified)':
return ChineseSimplifiedMnemonics.words;
case 'dutch':
return DutchMnemonics.words;
case 'german':
return GermanMnemonics.words;
case 'japanese':
return JapaneseMnemonics.words;
case 'portuguese':
return PortugueseMnemonics.words;
case 'russian':
return RussianMnemonics.words;
case 'spanish':
return SpanishMnemonics.words;
case 'french':
return FrenchMnemonics.words;
case 'italian':
return ItalianMnemonics.words;
default:
return EnglishMnemonics.words;
}
}
@override
TransactionPriority getDefaultTransactionPriority() {
return MoneroTransactionPriority.automatic;
}
@override
WalletCredentials createMoneroRestoreWalletFromKeysCredentials({
required String name,
required String spendKey,
required String viewKey,
required String address,
required String password,
required String language,
required int height}) {
return MoneroRestoreWalletFromKeysCredentials(
name: name,
spendKey: spendKey,
viewKey: viewKey,
address: address,
password: password,
language: language,
height: height);
}
@override
WalletCredentials createMoneroRestoreWalletFromSeedCredentials({
required String name,
required String password,
required int height,
required String mnemonic}) {
return MoneroRestoreWalletFromSeedCredentials(
name: name,
password: password,
height: height,
mnemonic: mnemonic);
}
@override
TransactionPriority getMoneroTransactionPrioritySlow() => MoneroTransactionPriority.slow;
@override
WalletCredentials createMoneroNewWalletCredentials({
@override
TransactionPriority getMoneroTransactionPriorityAutomatic() =>
MoneroTransactionPriority.automatic;
@override
TransactionPriority deserializeMoneroTransactionPriority({required int raw}) {
return MoneroTransactionPriority.deserialize(raw: raw);
}
@override
List<TransactionPriority> getTransactionPriorities() {
return MoneroTransactionPriority.all;
}
@override
List<String> getMoneroWordList(String language) {
switch (language.toLowerCase()) {
case 'english':
return EnglishMnemonics.words;
case 'chinese (simplified)':
return ChineseSimplifiedMnemonics.words;
case 'dutch':
return DutchMnemonics.words;
case 'german':
return GermanMnemonics.words;
case 'japanese':
return JapaneseMnemonics.words;
case 'portuguese':
return PortugueseMnemonics.words;
case 'russian':
return RussianMnemonics.words;
case 'spanish':
return SpanishMnemonics.words;
case 'french':
return FrenchMnemonics.words;
case 'italian':
return ItalianMnemonics.words;
default:
return EnglishMnemonics.words;
}
}
@override
WalletCredentials createMoneroRestoreWalletFromKeysCredentials(
{required String name,
required String spendKey,
required String viewKey,
required String address,
required String password,
required String language,
required int height}) {
return MoneroRestoreWalletFromKeysCredentials(
name: name,
spendKey: spendKey,
viewKey: viewKey,
address: address,
password: password,
language: language,
height: height);
}
@override
WalletCredentials createMoneroRestoreWalletFromSeedCredentials(
{required String name,
required String password,
required int height,
required String mnemonic}) {
return 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,
}) {
return MoneroNewWalletCredentials(name: name, password: password, language: language);
}
@override
Map<String, String> getKeys(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
final keys = moneroWallet.keys;
return <String, String>{
'privateSpendKey': keys.privateSpendKey,
@override
Map<String, String> getKeys(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
final keys = moneroWallet.keys;
return <String, String>{
'privateSpendKey': keys.privateSpendKey,
'privateViewKey': keys.privateViewKey,
'publicSpendKey': keys.publicSpendKey,
'publicViewKey': keys.publicViewKey};
}
'publicViewKey': keys.publicViewKey
};
}
@override
Object createMoneroTransactionCreationCredentials({
required List<Output> outputs,
required TransactionPriority priority}) {
return MoneroTransactionCreationCredentials(
outputs: outputs.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as MoneroTransactionPriority);
}
@override
Object createMoneroTransactionCreationCredentials(
{required List<Output> outputs, required TransactionPriority priority}) {
return MoneroTransactionCreationCredentials(
outputs: outputs
.map((out) => OutputInfo(
fiatAmount: out.fiatAmount,
cryptoAmount: out.cryptoAmount,
address: out.address,
note: out.note,
sendAll: out.sendAll,
extractedAddress: out.extractedAddress,
isParsedAddress: out.isParsedAddress,
formattedCryptoAmount: out.formattedCryptoAmount))
.toList(),
priority: priority as MoneroTransactionPriority);
}
@override
Object createMoneroTransactionCreationCredentialsRaw({
required List<OutputInfo> outputs,
required TransactionPriority priority}) {
return MoneroTransactionCreationCredentials(
outputs: outputs,
priority: priority as MoneroTransactionPriority);
}
@override
Object createMoneroTransactionCreationCredentialsRaw(
{required List<OutputInfo> outputs, required TransactionPriority priority}) {
return MoneroTransactionCreationCredentials(
outputs: outputs, priority: priority as MoneroTransactionPriority);
}
@override
String formatterMoneroAmountToString({required int amount}) {
return moneroAmountToString(amount: amount);
}
@override
String formatterMoneroAmountToString({required int amount}) {
return moneroAmountToString(amount: amount);
}
@override
double formatterMoneroAmountToDouble({required int amount}) {
return moneroAmountToDouble(amount: amount);
}
@override
double formatterMoneroAmountToDouble({required int amount}) {
return moneroAmountToDouble(amount: amount);
}
@override
int formatterMoneroParseAmount({required String amount}) {
return moneroParseAmount(amount: amount);
}
@override
int formatterMoneroParseAmount({required String amount}) {
return moneroParseAmount(amount: amount);
}
@override
Account getCurrentAccount(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
final acc = moneroWallet.walletAddresses.account;
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
}
@override
Account getCurrentAccount(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
final acc = moneroWallet.walletAddresses.account;
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
}
@override
void setCurrentAccount(Object wallet, int id, String label, String? balance) {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.account = monero_account.Account(id: id, label: label, balance: balance);
}
@override
void setCurrentAccount(Object wallet, int id, String label, String? balance) {
final moneroWallet = wallet as MoneroWallet;
moneroWallet.walletAddresses.account =
monero_account.Account(id: id, label: label, balance: balance);
}
@override
void onStartup() {
monero_wallet_api.onStartup();
}
@override
void onStartup() {
monero_wallet_api.onStartup();
}
@override
int getTransactionInfoAccountId(TransactionInfo tx) {
final moneroTransactionInfo = tx as MoneroTransactionInfo;
return moneroTransactionInfo.accountIndex;
}
@override
int getTransactionInfoAccountId(TransactionInfo tx) {
final moneroTransactionInfo = tx as MoneroTransactionInfo;
return moneroTransactionInfo.accountIndex;
}
@override
WalletService createMoneroWalletService(Box<WalletInfo> walletInfoSource) {
return MoneroWalletService(walletInfoSource);
}
@override
WalletService createMoneroWalletService(
Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource) {
return MoneroWalletService(walletInfoSource, unspentCoinSource);
}
@override
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.getTransactionAddress(accountIndex, addressIndex);
}
@override
String getTransactionAddress(Object wallet, int accountIndex, int addressIndex) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.getTransactionAddress(accountIndex, addressIndex);
}
@override
String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.getSubaddressLabel(accountIndex, addressIndex);
}
@override
String getSubaddressLabel(Object wallet, int accountIndex, int addressIndex) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.getSubaddressLabel(accountIndex, addressIndex);
}
@override
Map<String, String> pendingTransactionInfo(Object transaction) {
final ptx = transaction as PendingMoneroTransaction;
return {'id': ptx.id, 'hex': ptx.hex, 'key': ptx.txKey};
}
@override
Map<String, String> pendingTransactionInfo(Object transaction) {
final ptx = transaction as PendingMoneroTransaction;
return {'id': ptx.id, 'hex': ptx.hex, 'key': ptx.txKey};
}
@override
List<Unspent> getUnspents(Object wallet) {
final moneroWallet = wallet as MoneroWallet;
return moneroWallet.unspentCoins
.map((MoneroUnspent moneroUnspent) => Unspent(moneroUnspent.address, moneroUnspent.hash,
moneroUnspent.value, 0, moneroUnspent.keyImage))
.toList();
}
@override
void updateUnspents(Object wallet) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.updateUnspent();
}
}
+1 -1
View File
@@ -100,7 +100,7 @@ class SendPage extends BasePage {
AppBarStyle get appBarStyle => AppBarStyle.transparent;
double _sendCardHeight(BuildContext context) {
final double initialHeight = sendViewModel.isElectrumWallet ? 490 : 465;
final double initialHeight = sendViewModel.hasCoinControl ? 490 : 465;
if (!ResponsiveLayoutUtil.instance.isMobile) {
return initialHeight - 66;
+1 -1
View File
@@ -496,7 +496,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
),
),
),
if (sendViewModel.isElectrumWallet)
if (sendViewModel.hasCoinControl)
Padding(
padding: EdgeInsets.only(top: 6),
child: GestureDetector(
@@ -21,7 +21,6 @@ class TransactionDetailsPage extends BasePage {
@override
Widget body(BuildContext context) {
// FIX-ME: Added `context` it was not used here before, maby bug ?
return SectionStandardList(
sectionCount: 1,
itemCounter: (int _) => transactionDetailsViewModel.items.length,
@@ -6,11 +6,12 @@ import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class TextFieldListRow extends StatelessWidget {
TextFieldListRow(
{required this.title,
required this.value,
this.titleFontSize = 14,
this.valueFontSize = 16,
this.onSubmitted})
: _textController = TextEditingController() {
required this.value,
this.titleFontSize = 14,
this.valueFontSize = 16,
this.onSubmitted,
this.onTapOutside})
: _textController = TextEditingController() {
_textController.text = value;
}
@@ -19,6 +20,7 @@ class TextFieldListRow extends StatelessWidget {
final double titleFontSize;
final double valueFontSize;
final Function(String value)? onSubmitted;
final Function(String value)? onTapOutside;
final TextEditingController _textController;
@override
@@ -58,6 +60,7 @@ class TextFieldListRow extends StatelessWidget {
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor),
border: InputBorder.none),
onTapOutside: (_) => onTapOutside?.call(_textController.text),
onSubmitted: (value) => onSubmitted?.call(value),
)
]),
@@ -24,7 +24,6 @@ class UnspentCoinsDetailsPage extends BasePage {
@override
Widget body(BuildContext context) {
// FIX-ME: Added `context` it was not used here before, maby bug ?
return SectionStandardList(
sectionCount: 1,
itemCounter: (int _) => unspentCoinsDetailsViewModel.items.length,
@@ -45,6 +44,7 @@ class UnspentCoinsDetailsPage extends BasePage {
return TextFieldListRow(
title: item.title,
value: item.value,
onTapOutside: item.onSubmitted,
onSubmitted: item.onSubmitted,
);
}
@@ -94,7 +94,7 @@ class UnspentCoinsListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AutoSizeText(
address,
'${address.substring(0, 5)}...${address.substring(address.length-5)}', // ToDo: Maybe use address label
style: TextStyle(
color: addressColor,
fontSize: 12,
+6 -2
View File
@@ -172,6 +172,10 @@ abstract class SendViewModelBase with Store {
.where((template) => _isEqualCurrency(template.cryptoCurrency))
.toList();
@computed
bool get hasCoinControl =>
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin || _wallet.type == WalletType.monero;
@computed
bool get isElectrumWallet =>
_wallet.type == WalletType.bitcoin || _wallet.type == WalletType.litecoin;
@@ -206,12 +210,12 @@ abstract class SendViewModelBase with Store {
@computed
List<ContactRecord> get contactsToShow => contactListViewModel.contacts
.where((element) => selectedCryptoCurrency == null || element.type == selectedCryptoCurrency)
.where((element) => element.type == selectedCryptoCurrency)
.toList();
@computed
List<WalletContact> get walletContactsToShow => contactListViewModel.walletContacts
.where((element) => selectedCryptoCurrency == null || element.type == selectedCryptoCurrency)
.where((element) => element.type == selectedCryptoCurrency)
.toList();
@action
@@ -23,6 +23,7 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
note = unspentCoinsItem.note {
items = [
StandartListItem(title: S.current.transaction_details_amount, value: unspentCoinsItem.amount),
StandartListItem(title: S.current.transaction_details_transaction_id, value: unspentCoinsItem.hash),
StandartListItem(title: S.current.widgets_address, value: unspentCoinsItem.address),
TextFieldListItem(
title: S.current.note_tap_to_change,
@@ -42,19 +43,22 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
unspentCoinsItem.isSending = !value;
}
await unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem);
}),
BlockExplorerListItem(
title: S.current.view_in_block_explorer,
value: _explorerDescription(unspentCoinsListViewModel.wallet.type),
onTap: () {
try {
final url = Uri.parse(
_explorerUrl(unspentCoinsListViewModel.wallet.type, unspentCoinsItem.hash));
return launchUrl(url);
} catch (e) {}
})
];
if ([WalletType.bitcoin, WalletType.litecoin].contains(unspentCoinsListViewModel.wallet.type)) {
items.add(BlockExplorerListItem(
title: S.current.view_in_block_explorer,
value: _explorerDescription(unspentCoinsListViewModel.wallet.type),
onTap: () {
try {
final url = Uri.parse(
_explorerUrl(unspentCoinsListViewModel.wallet.type, unspentCoinsItem.hash));
return launchUrl(url);
} catch (e) {}
},
));
}
}
String _explorerUrl(WalletType type, String txId) {
@@ -13,7 +13,9 @@ abstract class UnspentCoinsItemBase with Store {
required this.note,
required this.isSending,
required this.amountRaw,
required this.vout});
required this.vout,
required this.keyImage
});
@observable
String address;
@@ -38,4 +40,7 @@ abstract class UnspentCoinsItemBase with Store {
@observable
int vout;
}
@observable
String? keyImage;
}
@@ -1,10 +1,15 @@
import 'package:cw_core/unspent_coins_info.dart';
import 'package:collection/collection.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/entities/unspent_transaction_output.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:cw_bitcoin/bitcoin_wallet.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_monero/monero_wallet.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:collection/collection.dart';
part 'unspent_coins_list_view_model.g.dart';
@@ -14,7 +19,7 @@ abstract class UnspentCoinsListViewModelBase with Store {
UnspentCoinsListViewModelBase(
{required this.wallet, required Box<UnspentCoinsInfo> unspentCoinsInfo})
: _unspentCoinsInfo = unspentCoinsInfo {
bitcoin!.updateUnspents(wallet);
_updateUnspents();
}
WalletBase wallet;
@@ -22,11 +27,10 @@ abstract class UnspentCoinsListViewModelBase with Store {
@computed
ObservableList<UnspentCoinsItem> get items =>
ObservableList.of(bitcoin!.getUnspents(wallet).map((elem) {
final amount = bitcoin!.formatterBitcoinAmountToString(amount: elem.value) +
' ${wallet.currency.title}';
ObservableList.of(_getUnspents().map((elem) {
final amount = formatAmountToString(elem.value) + ' ${wallet.currency.title}';
final info = getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout);
final info = getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
return UnspentCoinsItem(
address: elem.address,
@@ -36,12 +40,14 @@ abstract class UnspentCoinsListViewModelBase with Store {
note: info?.note ?? '',
isSending: info?.isSending ?? true,
amountRaw: elem.value,
vout: elem.vout);
vout: elem.vout,
keyImage: elem.keyImage
);
}));
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
try {
final info = getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout);
final info = getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage);
if (info == null) {
final newInfo = UnspentCoinsInfo(
walletId: wallet.id,
@@ -51,10 +57,12 @@ abstract class UnspentCoinsListViewModelBase with Store {
vout: item.vout,
isFrozen: item.isFrozen,
isSending: item.isSending,
noteRaw: item.note);
noteRaw: item.note,
keyImage: item.keyImage
);
await _unspentCoinsInfo.add(newInfo);
bitcoin!.updateUnspents(wallet);
_updateUnspents();
wallet.updateBalance();
return;
}
@@ -63,19 +71,45 @@ abstract class UnspentCoinsListViewModelBase with Store {
info.note = item.note;
await info.save();
bitcoin!.updateUnspents(wallet);
_updateUnspents();
wallet.updateBalance();
} catch (e) {
print(e.toString());
}
}
UnspentCoinsInfo? getUnspentCoinInfo(String hash, String address, int value, int vout) {
UnspentCoinsInfo? getUnspentCoinInfo(String hash, String address, int value, int vout, String? keyImage) {
return _unspentCoinsInfo.values.firstWhereOrNull((element) =>
element.walletId == wallet.id &&
element.hash == hash &&
element.address == address &&
element.value == value &&
element.vout == vout);
element.vout == vout &&
element.keyImage == keyImage
);
}
String formatAmountToString(int fullBalance) {
if (wallet.type == WalletType.monero)
return monero!.formatterMoneroAmountToString(amount: fullBalance);
if ([WalletType.bitcoin, WalletType.litecoin].contains(wallet.type))
return bitcoin!.formatterBitcoinAmountToString(amount: fullBalance);
return '';
}
void _updateUnspents() {
if (wallet.type == WalletType.monero)
return monero!.updateUnspents(wallet);
if ([WalletType.bitcoin, WalletType.litecoin].contains(wallet.type))
return bitcoin!.updateUnspents(wallet);
}
List<Unspent> _getUnspents() {
if (wallet.type == WalletType.monero)
return monero!.getUnspents(wallet);
if ([WalletType.bitcoin, WalletType.litecoin].contains(wallet.type))
return bitcoin!.getUnspents(wallet);
return List.empty();
}
}