mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
CW-766 fix coin freezing (#1751)
* fix coin freezing * fix frozen balance * update monero_c hash * update monero_c hash (after merge) * fix test E make it ready * revert local change * throw error on wow as well * experiment with view model code * move view model into di.dart
This commit is contained in:
parent
9706243c81
commit
87178b2a54
8 changed files with 92 additions and 28 deletions
|
@ -12,6 +12,18 @@ int countOfCoins() => monero.Coins_count(coins!);
|
||||||
|
|
||||||
monero.CoinsInfo getCoin(int index) => monero.Coins_coin(coins!, index);
|
monero.CoinsInfo getCoin(int index) => monero.Coins_coin(coins!, index);
|
||||||
|
|
||||||
|
int? getCoinByKeyImage(String keyImage) {
|
||||||
|
final count = countOfCoins();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
final coin = getCoin(i);
|
||||||
|
final coinAddress = monero.CoinsInfo_keyImage(coin);
|
||||||
|
if (keyImage == coinAddress) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void freezeCoin(int index) => monero.Coins_setFrozen(coins!, index: index);
|
void freezeCoin(int index) => monero.Coins_setFrozen(coins!, index: index);
|
||||||
|
|
||||||
void thawCoin(int index) => monero.Coins_thaw(coins!, index: index);
|
void thawCoin(int index) => monero.Coins_thaw(coins!, index: index);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cw_monero/api/exceptions/creation_transaction_exception.dart';
|
||||||
import 'package:cw_monero/api/monero_output.dart';
|
import 'package:cw_monero/api/monero_output.dart';
|
||||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||||
import 'package:cw_monero/api/wallet.dart';
|
import 'package:cw_monero/api/wallet.dart';
|
||||||
|
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen;
|
import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen;
|
||||||
|
@ -101,7 +102,11 @@ Future<PendingTransactionDescription> createTransactionSync(
|
||||||
});
|
});
|
||||||
|
|
||||||
final address_ = address.toNativeUtf8();
|
final address_ = address.toNativeUtf8();
|
||||||
final paymentId_ = paymentId.toNativeUtf8();
|
final paymentId_ = paymentId.toNativeUtf8();
|
||||||
|
if (preferredInputs.isEmpty) {
|
||||||
|
throw MoneroTransactionCreationException("No inputs provided, transaction cannot be constructed");
|
||||||
|
}
|
||||||
|
|
||||||
final preferredInputs_ = preferredInputs.join(monero.defaultSeparatorStr).toNativeUtf8();
|
final preferredInputs_ = preferredInputs.join(monero.defaultSeparatorStr).toNativeUtf8();
|
||||||
|
|
||||||
final addraddr = address_.address;
|
final addraddr = address_.address;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:cw_monero/api/account_list.dart';
|
import 'package:cw_monero/api/account_list.dart';
|
||||||
import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
|
import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:monero/monero.dart' as monero;
|
import 'package:monero/monero.dart' as monero;
|
||||||
import 'package:mutex/mutex.dart';
|
import 'package:mutex/mutex.dart';
|
||||||
|
|
||||||
|
@ -129,6 +130,15 @@ Future<bool> setupNodeSync(
|
||||||
throw SetupWalletException(message: error);
|
throw SetupWalletException(message: error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kDebugMode) {
|
||||||
|
monero.Wallet_init3(
|
||||||
|
wptr!, argv0: '',
|
||||||
|
defaultLogBaseName: 'moneroc',
|
||||||
|
console: true,
|
||||||
|
logPath: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return status == 0;
|
return status == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,32 @@
|
||||||
import 'package:cw_core/unspent_transaction_output.dart';
|
import 'package:cw_core/unspent_transaction_output.dart';
|
||||||
|
import 'package:cw_monero/api/coins_info.dart';
|
||||||
|
import 'package:monero/monero.dart' as monero;
|
||||||
|
|
||||||
class MoneroUnspent extends Unspent {
|
class MoneroUnspent extends Unspent {
|
||||||
MoneroUnspent(
|
MoneroUnspent(
|
||||||
String address, String hash, String keyImage, int value, bool isFrozen, this.isUnlocked)
|
String address, String hash, String keyImage, int value, bool isFrozen, this.isUnlocked)
|
||||||
: super(address, hash, value, 0, keyImage) {
|
: super(address, hash, value, 0, keyImage) {
|
||||||
this.isFrozen = isFrozen;
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
set isFrozen(bool freeze) {
|
||||||
|
print("set isFrozen: $freeze ($keyImage): $freeze");
|
||||||
|
final coinId = getCoinByKeyImage(keyImage!);
|
||||||
|
if (coinId == null) throw Exception("Unable to find a coin for address $address");
|
||||||
|
if (freeze) {
|
||||||
|
freezeCoin(coinId);
|
||||||
|
} else {
|
||||||
|
thawCoin(coinId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isFrozen {
|
||||||
|
print("get isFrozen");
|
||||||
|
final coinId = getCoinByKeyImage(keyImage!);
|
||||||
|
if (coinId == null) throw Exception("Unable to find a coin for address $address");
|
||||||
|
final coin = getCoin(coinId);
|
||||||
|
return monero.CoinsInfo_frozen(coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
final bool isUnlocked;
|
final bool isUnlocked;
|
||||||
|
|
|
@ -309,9 +309,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
'You do not have enough XMR to send this amount.');
|
'You do not have enough XMR to send this amount.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spendAllCoins && (allInputsAmount < totalAmount + estimatedFee)) {
|
if (inputs.isEmpty) MoneroTransactionCreationException(
|
||||||
throw MoneroTransactionNoInputsException(inputs.length);
|
'No inputs selected');
|
||||||
}
|
|
||||||
|
|
||||||
final moneroOutputs = outputs.map((output) {
|
final moneroOutputs = outputs.map((output) {
|
||||||
final outputAddress =
|
final outputAddress =
|
||||||
|
@ -337,23 +336,18 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
final formattedAmount =
|
final formattedAmount =
|
||||||
output.sendAll ? null : output.formattedCryptoAmount;
|
output.sendAll ? null : output.formattedCryptoAmount;
|
||||||
|
|
||||||
if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
|
// if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
|
||||||
(formattedAmount == null && unlockedBalance <= 0)) {
|
// (formattedAmount == null && unlockedBalance <= 0)) {
|
||||||
final formattedBalance = moneroAmountToString(amount: unlockedBalance);
|
// final formattedBalance = moneroAmountToString(amount: unlockedBalance);
|
||||||
|
//
|
||||||
throw MoneroTransactionCreationException(
|
// throw MoneroTransactionCreationException(
|
||||||
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
|
// 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
|
||||||
}
|
// }
|
||||||
|
|
||||||
final estimatedFee =
|
final estimatedFee =
|
||||||
calculateEstimatedFee(_credentials.priority, formattedAmount);
|
calculateEstimatedFee(_credentials.priority, formattedAmount);
|
||||||
if (!spendAllCoins &&
|
if (inputs.isEmpty) MoneroTransactionCreationException(
|
||||||
((formattedAmount != null &&
|
'No inputs selected');
|
||||||
allInputsAmount < (formattedAmount + estimatedFee)) ||
|
|
||||||
formattedAmount == null)) {
|
|
||||||
throw MoneroTransactionNoInputsException(inputs.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
pendingTransactionDescription =
|
pendingTransactionDescription =
|
||||||
await transaction_history.createTransaction(
|
await transaction_history.createTransaction(
|
||||||
address: address!,
|
address: address!,
|
||||||
|
@ -363,6 +357,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
preferredInputs: inputs);
|
preferredInputs: inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// final status = monero.PendingTransaction_status(pendingTransactionDescription);
|
||||||
|
|
||||||
return PendingMoneroTransaction(pendingTransactionDescription);
|
return PendingMoneroTransaction(pendingTransactionDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +511,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
for (var i = 0; i < coinCount; i++) {
|
for (var i = 0; i < coinCount; i++) {
|
||||||
final coin = getCoin(i);
|
final coin = getCoin(i);
|
||||||
final coinSpent = monero.CoinsInfo_spent(coin);
|
final coinSpent = monero.CoinsInfo_spent(coin);
|
||||||
if (coinSpent == false) {
|
if (coinSpent == false && monero.CoinsInfo_subaddrAccount(coin) == walletAddresses.account!.id) {
|
||||||
final unspent = MoneroUnspent(
|
final unspent = MoneroUnspent(
|
||||||
monero.CoinsInfo_address(coin),
|
monero.CoinsInfo_address(coin),
|
||||||
monero.CoinsInfo_hash(coin),
|
monero.CoinsInfo_hash(coin),
|
||||||
|
@ -729,9 +725,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
|
|
||||||
void _askForUpdateBalance() {
|
void _askForUpdateBalance() {
|
||||||
final unlockedBalance = _getUnlockedBalance();
|
final unlockedBalance = _getUnlockedBalance();
|
||||||
final fullBalance = _getFullBalance();
|
final fullBalance = _getUnlockedBalance() + _getFrozenBalance();
|
||||||
final frozenBalance = _getFrozenBalance();
|
final frozenBalance = 0; // this is calculated on the monero side now
|
||||||
|
|
||||||
if (balance[currency]!.fullBalance != fullBalance ||
|
if (balance[currency]!.fullBalance != fullBalance ||
|
||||||
balance[currency]!.unlockedBalance != unlockedBalance ||
|
balance[currency]!.unlockedBalance != unlockedBalance ||
|
||||||
balance[currency]!.frozenBalance != frozenBalance) {
|
balance[currency]!.frozenBalance != frozenBalance) {
|
||||||
|
@ -757,9 +752,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
for (var coin in unspentCoinsInfo.values.where((element) =>
|
for (var coin in unspentCoinsInfo.values.where((element) =>
|
||||||
element.walletId == id &&
|
element.walletId == id &&
|
||||||
element.accountIndex == walletAddresses.account!.id)) {
|
element.accountIndex == walletAddresses.account!.id)) {
|
||||||
if (coin.isFrozen) frozenBalance += coin.value;
|
if (coin.isFrozen && !coin.isSending) frozenBalance += coin.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return frozenBalance;
|
return frozenBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,16 @@ import 'package:cw_wownero/api/exceptions/creation_transaction_exception.dart';
|
||||||
import 'package:cw_wownero/api/wallet.dart';
|
import 'package:cw_wownero/api/wallet.dart';
|
||||||
import 'package:cw_wownero/api/wownero_output.dart';
|
import 'package:cw_wownero/api/wownero_output.dart';
|
||||||
import 'package:cw_wownero/api/structs/pending_transaction.dart';
|
import 'package:cw_wownero/api/structs/pending_transaction.dart';
|
||||||
|
import 'package:cw_wownero/exceptions/wownero_transaction_creation_exception.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:monero/wownero.dart' as wownero;
|
import 'package:monero/wownero.dart' as wownero;
|
||||||
import 'package:monero/src/generated_bindings_wownero.g.dart' as wownero_gen;
|
import 'package:monero/src/generated_bindings_wownero.g.dart' as wownero_gen;
|
||||||
|
|
||||||
|
|
||||||
String getTxKey(String txId) {
|
String getTxKey(String txId) {
|
||||||
return wownero.Wallet_getTxKey(wptr!, txid: txId);
|
final ret = wownero.Wallet_getTxKey(wptr!, txid: txId);
|
||||||
|
wownero.Wallet_status(wptr!);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
wownero.TransactionHistory? txhistory;
|
wownero.TransactionHistory? txhistory;
|
||||||
|
@ -86,7 +89,10 @@ Future<PendingTransactionDescription> createTransactionSync(
|
||||||
final amt = amount == null ? 0 : wownero.Wallet_amountFromString(amount);
|
final amt = amount == null ? 0 : wownero.Wallet_amountFromString(amount);
|
||||||
|
|
||||||
final address_ = address.toNativeUtf8();
|
final address_ = address.toNativeUtf8();
|
||||||
final paymentId_ = paymentId.toNativeUtf8();
|
final paymentId_ = paymentId.toNativeUtf8();
|
||||||
|
if (preferredInputs.isEmpty) {
|
||||||
|
throw WowneroTransactionCreationException("No inputs provided, transaction cannot be constructed");
|
||||||
|
}
|
||||||
final preferredInputs_ = preferredInputs.join(wownero.defaultSeparatorStr).toNativeUtf8();
|
final preferredInputs_ = preferredInputs.join(wownero.defaultSeparatorStr).toNativeUtf8();
|
||||||
|
|
||||||
final waddr = wptr!.address;
|
final waddr = wptr!.address;
|
||||||
|
|
|
@ -746,6 +746,7 @@ Future<void> setup({
|
||||||
_transactionDescriptionBox,
|
_transactionDescriptionBox,
|
||||||
getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null,
|
getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null,
|
||||||
coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.any,
|
coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.any,
|
||||||
|
getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import 'package:cake_wallet/tron/tron.dart';
|
||||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
||||||
import 'package:cake_wallet/wownero/wownero.dart';
|
import 'package:cake_wallet/wownero/wownero.dart';
|
||||||
import 'package:cw_core/exceptions.dart';
|
import 'package:cw_core/exceptions.dart';
|
||||||
import 'package:cw_core/transaction_info.dart';
|
import 'package:cw_core/transaction_info.dart';
|
||||||
|
@ -64,6 +65,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
wallet.type == WalletType.tron;
|
wallet.type == WalletType.tron;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||||
|
|
||||||
SendViewModelBase(
|
SendViewModelBase(
|
||||||
AppStore appStore,
|
AppStore appStore,
|
||||||
this.sendTemplateViewModel,
|
this.sendTemplateViewModel,
|
||||||
|
@ -71,7 +74,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
this.balanceViewModel,
|
this.balanceViewModel,
|
||||||
this.contactListViewModel,
|
this.contactListViewModel,
|
||||||
this.transactionDescriptionBox,
|
this.transactionDescriptionBox,
|
||||||
this.ledgerViewModel, {
|
this.ledgerViewModel,
|
||||||
|
this.unspentCoinsListViewModel, {
|
||||||
this.coinTypeToSpendFrom = UnspentCoinType.any,
|
this.coinTypeToSpendFrom = UnspentCoinType.any,
|
||||||
}) : state = InitialExecutionState(),
|
}) : state = InitialExecutionState(),
|
||||||
currencies = appStore.wallet!.balance.keys.toList(),
|
currencies = appStore.wallet!.balance.keys.toList(),
|
||||||
|
@ -530,6 +534,16 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
throw Exception('Priority is null for wallet type: ${wallet.type}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasCoinControl) {
|
||||||
|
bool isCoinSelected = false;
|
||||||
|
for (var coin in unspentCoinsListViewModel.items) {
|
||||||
|
isCoinSelected = isCoinSelected || (coin.isSending && !coin.isFrozen);
|
||||||
|
}
|
||||||
|
if (!isCoinSelected) {
|
||||||
|
throw Exception("No coin selected in coin control, you need to select a coin in order to spend");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (wallet.type) {
|
switch (wallet.type) {
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
|
|
Loading…
Reference in a new issue