Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CW-766 fix coin freezing #1751

Merged
merged 12 commits into from
Nov 27, 2024
12 changes: 12 additions & 0 deletions cw_monero/lib/api/coins_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ int countOfCoins() => monero.Coins_count(coins!);

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 thawCoin(int index) => monero.Coins_thaw(coins!, index: index);
7 changes: 6 additions & 1 deletion cw_monero/lib/api/transaction_history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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/structs/pending_transaction.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:monero/monero.dart' as monero;
import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen;
Expand Down Expand Up @@ -93,7 +94,11 @@ Future<PendingTransactionDescription> createTransactionSync(
final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount);

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 waddr = wptr!.address;
Expand Down
10 changes: 10 additions & 0 deletions cw_monero/lib/api/wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:isolate';

import 'package:cw_monero/api/account_list.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:mutex/mutex.dart';

Expand Down Expand Up @@ -129,6 +130,15 @@ Future<bool> setupNodeSync(
throw SetupWalletException(message: error);
}

if (kDebugMode) {
monero.Wallet_init3(
wptr!, argv0: '',
defaultLogBaseName: 'moneroc',
console: true,
logPath: '',
);
}

OmarHatem28 marked this conversation as resolved.
Show resolved Hide resolved
return status == 0;
}

Expand Down
24 changes: 23 additions & 1 deletion cw_monero/lib/monero_unspent.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
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 {
MoneroUnspent(
String address, String hash, String keyImage, int value, bool isFrozen, this.isUnlocked)
: 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;
Expand Down
40 changes: 17 additions & 23 deletions cw_monero/lib/monero_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
'You do not have enough XMR to send this amount.');
}

if (!spendAllCoins && (allInputsAmount < totalAmount + estimatedFee)) {
throw MoneroTransactionNoInputsException(inputs.length);
}
if (inputs.isEmpty) MoneroTransactionCreationException(
'No inputs selected');

final moneroOutputs = outputs.map((output) {
final outputAddress =
Expand All @@ -337,23 +336,18 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final formattedAmount =
output.sendAll ? null : output.formattedCryptoAmount;

if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
(formattedAmount == null && unlockedBalance <= 0)) {
final formattedBalance = moneroAmountToString(amount: unlockedBalance);

throw MoneroTransactionCreationException(
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
}
// if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
// (formattedAmount == null && unlockedBalance <= 0)) {
// final formattedBalance = moneroAmountToString(amount: unlockedBalance);
//
// throw MoneroTransactionCreationException(
// 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
// }

final estimatedFee =
calculateEstimatedFee(_credentials.priority, formattedAmount);
if (!spendAllCoins &&
((formattedAmount != null &&
allInputsAmount < (formattedAmount + estimatedFee)) ||
formattedAmount == null)) {
throw MoneroTransactionNoInputsException(inputs.length);
}

if (inputs.isEmpty) MoneroTransactionCreationException(
'No inputs selected');
pendingTransactionDescription =
await transaction_history.createTransaction(
address: address!,
Expand All @@ -363,6 +357,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
preferredInputs: inputs);
}

// final status = monero.PendingTransaction_status(pendingTransactionDescription);

return PendingMoneroTransaction(pendingTransactionDescription);
}

Expand Down Expand Up @@ -515,7 +511,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
for (var i = 0; i < coinCount; i++) {
final coin = getCoin(i);
final coinSpent = monero.CoinsInfo_spent(coin);
if (coinSpent == false) {
if (coinSpent == false && monero.CoinsInfo_subaddrAccount(coin) == walletAddresses.account!.id) {
final unspent = MoneroUnspent(
monero.CoinsInfo_address(coin),
monero.CoinsInfo_hash(coin),
Expand Down Expand Up @@ -729,9 +725,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,

void _askForUpdateBalance() {
final unlockedBalance = _getUnlockedBalance();
final fullBalance = _getFullBalance();
final frozenBalance = _getFrozenBalance();

final fullBalance = _getUnlockedBalance() + _getFrozenBalance();
final frozenBalance = 0; // this is calculated on the monero side now
OmarHatem28 marked this conversation as resolved.
Show resolved Hide resolved
if (balance[currency]!.fullBalance != fullBalance ||
balance[currency]!.unlockedBalance != unlockedBalance ||
balance[currency]!.frozenBalance != frozenBalance) {
Expand All @@ -757,9 +752,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
for (var coin in unspentCoinsInfo.values.where((element) =>
element.walletId == id &&
element.accountIndex == walletAddresses.account!.id)) {
if (coin.isFrozen) frozenBalance += coin.value;
if (coin.isFrozen && !coin.isSending) frozenBalance += coin.value;
}

return frozenBalance;
}

Expand Down
10 changes: 8 additions & 2 deletions cw_wownero/lib/api/transaction_history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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/wownero_output.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:monero/wownero.dart' as wownero;
import 'package:monero/src/generated_bindings_wownero.g.dart' as wownero_gen;


String getTxKey(String txId) {
return wownero.Wallet_getTxKey(wptr!, txid: txId);
final ret = wownero.Wallet_getTxKey(wptr!, txid: txId);
wownero.Wallet_status(wptr!);
OmarHatem28 marked this conversation as resolved.
Show resolved Hide resolved
return ret;
}

wownero.TransactionHistory? txhistory;
Expand Down Expand Up @@ -86,7 +89,10 @@ Future<PendingTransactionDescription> createTransactionSync(
final amt = amount == null ? 0 : wownero.Wallet_amountFromString(amount);

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 waddr = wptr!.address;
Expand Down
12 changes: 12 additions & 0 deletions lib/view_model/send/send_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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/dashboard/balance_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:cw_core/exceptions.dart';
import 'package:cw_core/transaction_info.dart';
Expand Down Expand Up @@ -530,6 +531,17 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
throw Exception('Priority is null for wallet type: ${wallet.type}');
}

if (hasCoinControl) {
final vm = getIt.get<UnspentCoinsListViewModel>(param1: coinTypeToSpendFrom);
MrCyjaneK marked this conversation as resolved.
Show resolved Hide resolved
bool isCoinSelected = false;
for (var coin in vm.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) {
case WalletType.bitcoin:
case WalletType.litecoin:
Expand Down
Loading