* - Add Decred Wallet
- Remove Haven Wallet
- Fix and Improve Solana Wallet
- Improve app usability and user experience
- User interface enhancements
- Bug fixes

* Release candidate feedback fixes

* Release candidate feedback fixes

* update release notes [skip ci]

* fix iOS stupidity [skip ci]

* minor fix

* decred: Do not create log file. (#2106) (#2107)

Co-authored-by: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>

* fix Monero HWW

* fix Monero view only wallet

* fix background sync for hardware and viewonly wallets

* check for status on correct wptr

* minor fixes [skip ci]

---------

Co-authored-by: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>
Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
This commit is contained in:
Omar Hatem 2025-03-25 03:49:54 +02:00 committed by GitHub
parent 13dc6de8dd
commit 0aa563ead7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 304 additions and 290 deletions

View file

@ -3,7 +3,6 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:async';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_decred/api/libdcrwallet_bindings.dart';
import 'package:cw_decred/api/util.dart';
@ -79,10 +78,12 @@ class Libwallet {
switch (method) {
case "initlibdcrwallet":
final logDir = args["logdir"] ?? "";
final level = args["level"] ?? "";
final cLogDir = logDir.toCString();
final cLevel = level.toCString();
executePayloadFn(
fn: () => dcrwalletApi.initialize(cLogDir),
ptrsToFree: [cLogDir],
fn: () => dcrwalletApi.initialize(cLogDir, cLevel),
ptrsToFree: [cLogDir, cLevel],
);
break;
case "createwallet":
@ -300,7 +301,7 @@ class Libwallet {
break;
case "shutdown":
final name = args["name"] ?? "";
final cName = name.toCString();
// final cName = name.toCString();
executePayloadFn(
fn: () => dcrwalletApi.shutdown(),
ptrsToFree: [],
@ -326,8 +327,8 @@ class Libwallet {
// initLibdcrwallet initializes libdcrwallet using the provided logDir and gets
// it ready for use. This must be done before attempting to create, load or use
// a wallet.
Future<void> initLibdcrwallet(String logDir) async {
// a wallet. An empty string can be used to log to stdout and create no log files.
Future<void> initLibdcrwallet(String logDir, String level) async {
if (_closed) throw StateError('Closed');
final completer = Completer<Object?>.sync();
final id = _idCounter++;
@ -335,6 +336,7 @@ class Libwallet {
final req = {
"method": "initlibdcrwallet",
"logdir": logDir,
"level": level,
};
_commands.send((id, req));
await completer.future;
@ -463,7 +465,11 @@ class Libwallet {
};
_commands.send((id, req));
final res = await completer.future as PayloadResult;
return jsonDecode(res.payload);
try {
return jsonDecode(res.payload);
} catch (_) {
return {};
}
}
Future<String> estimateFee(String walletName, int numBlocks) async {

View file

@ -380,7 +380,7 @@ abstract class DecredWalletBase
totalAmt = totalIn;
} else if (out.cryptoAmount != null) {
final coins = double.parse(out.cryptoAmount!);
amt = (coins * 1e8).toInt();
amt = (coins * 1e8).round();
}
totalAmt += amt;
final o = {
@ -415,7 +415,7 @@ abstract class DecredWalletBase
};
final fee = decoded["fee"] ?? 0;
if (sendAll) {
totalAmt = (totalAmt - fee).toInt();
totalAmt = (totalAmt - fee).round();
}
return DecredPendingTransaction(
txid: decoded["txid"] ?? "", amount: totalAmt, fee: fee, rawHex: signedHex, send: send);
@ -475,36 +475,41 @@ abstract class DecredWalletBase
}
Future<Map<String, DecredTransactionInfo>> fetchFiveTransactions(int from) async {
final res = await _libwallet.listTransactions(walletInfo.name, from.toString(), "5");
final decoded = json.decode(res);
var txs = <String, DecredTransactionInfo>{};
for (final d in decoded) {
final txid = uniqueTxID(d["txid"] ?? "", d["vout"] ?? 0);
var direction = TransactionDirection.outgoing;
if (d["category"] == "receive") {
direction = TransactionDirection.incoming;
try {
final res = await _libwallet.listTransactions(walletInfo.name, from.toString(), "5");
final decoded = json.decode(res);
var txs = <String, DecredTransactionInfo>{};
for (final d in decoded) {
final txid = uniqueTxID(d["txid"] ?? "", d["vout"] ?? 0);
var direction = TransactionDirection.outgoing;
if (d["category"] == "receive") {
direction = TransactionDirection.incoming;
}
final amountDouble = d["amount"] ?? 0.0;
final amount = (amountDouble * 1e8).round().abs();
final feeDouble = d["fee"] ?? 0.0;
final fee = (feeDouble * 1e8).round().abs();
final confs = d["confirmations"] ?? 0;
final sendTime = d["time"] ?? 0;
final height = d["height"] ?? 0;
final txInfo = DecredTransactionInfo(
id: txid,
amount: amount,
fee: fee,
direction: direction,
isPending: confs == 0,
date: DateTime.fromMillisecondsSinceEpoch(sendTime * 1000, isUtc: false),
height: height,
confirmations: confs,
to: d["address"] ?? "",
);
txs[txid] = txInfo;
}
final amountDouble = d["amount"] ?? 0.0;
final amount = (amountDouble * 1e8).toInt().abs();
final feeDouble = d["fee"] ?? 0.0;
final fee = (feeDouble * 1e8).toInt().abs();
final confs = d["confirmations"] ?? 0;
final sendTime = d["time"] ?? 0;
final height = d["height"] ?? 0;
final txInfo = DecredTransactionInfo(
id: txid,
amount: amount,
fee: fee,
direction: direction,
isPending: confs == 0,
date: DateTime.fromMillisecondsSinceEpoch(sendTime * 1000, isUtc: false),
height: height,
confirmations: confs,
to: d["address"] ?? "",
);
txs[txid] = txInfo;
return txs;
} catch (e) {
printV(e);
return {};
}
return txs;
}
// uniqueTxID combines the tx id and vout to create a unique id.
@ -612,21 +617,25 @@ abstract class DecredWalletBase
}
Future<void> fetchUnspents() async {
final res = await _libwallet.listUnspents(walletInfo.name);
final decoded = json.decode(res);
var unspents = <Unspent>[];
for (final d in decoded) {
final spendable = d["spendable"] ?? false;
if (!spendable) {
continue;
try {
final res = await _libwallet.listUnspents(walletInfo.name);
final decoded = json.decode(res);
var unspents = <Unspent>[];
for (final d in decoded) {
final spendable = d["spendable"] ?? false;
if (!spendable) {
continue;
}
final amountDouble = d["amount"] ?? 0.0;
final amount = (amountDouble * 1e8).round().abs();
final utxo = Unspent(d["address"] ?? "", d["txid"] ?? "", amount, d["vout"] ?? 0, null);
utxo.isChange = d["ischange"] ?? false;
unspents.add(utxo);
}
final amountDouble = d["amount"] ?? 0.0;
final amount = (amountDouble * 1e8).toInt().abs();
final utxo = Unspent(d["address"] ?? "", d["txid"] ?? "", amount, d["vout"] ?? 0, null);
utxo.isChange = d["ischange"] ?? false;
unspents.add(utxo);
_unspents = unspents;
} catch (e) {
printV(e);
}
_unspents = unspents;
}
List<Unspent> unspents() {

View file

@ -1,4 +1,5 @@
import 'dart:convert';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/address_info.dart';
@ -103,13 +104,18 @@ abstract class DecredWalletAddressesBase extends WalletAddresses with Store {
if (this.isEnabledAutoGenerateSubaddress) {
nUnused = "3";
}
final res = await _libwallet.addresses(walletInfo.name, nUsed, nUnused);
final decoded = json.decode(res);
final usedAddrs = List<String>.from(decoded["used"] ?? []);
final unusedAddrs = List<String>.from(decoded["unused"] ?? []);
// index is the index of the first unused address.
final index = decoded["index"] ?? 0;
return new LibAddresses(usedAddrs, unusedAddrs, index);
try {
final res = await _libwallet.addresses(walletInfo.name, nUsed, nUnused);
final decoded = json.decode(res);
final usedAddrs = List<String>.from(decoded["used"] ?? []);
final unusedAddrs = List<String>.from(decoded["unused"] ?? []);
// index is the index of the first unused address.
final index = decoded["index"] ?? 0;
return new LibAddresses(usedAddrs, unusedAddrs, index);
} catch (e) {
printV(e);
return LibAddresses([], [], 0);
}
}
Future<void> generateNewAddress(String label) async {

View file

@ -27,17 +27,16 @@ class DecredWalletService extends WalletService<
static final pubkeyRestorePathTestnet = "m/44'/1'/0'";
final mainnet = "mainnet";
final testnet = "testnet";
Libwallet? libwallet;
static Libwallet? libwallet;
Future<void> init() async {
if (libwallet != null) {
return;
}
libwallet = await Libwallet.spawn();
// Use the general path for all dcr wallets as the general log directory.
// Individual wallet paths may be removed if the wallet is deleted.
final dcrLogDir = await pathForWalletDir(name: '', type: WalletType.decred);
libwallet!.initLibdcrwallet(dcrLogDir);
// Init logging with no directory to force printing to stdout and only
// print ERROR level logs.
libwallet!.initLibdcrwallet("", "err");
}
void closeLibwallet() {

View file

@ -75,18 +75,18 @@ packages:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
version: "4.0.2"
build_resolvers:
dependency: "direct dev"
description:
@ -365,10 +365,10 @@ packages:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
version: "4.0.2"
intl:
dependency: transitive
description:
@ -646,10 +646,10 @@ packages:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.2"
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
@ -848,5 +848,5 @@ packages:
source: hosted
version: "2.2.2"
sdks:
dart: ">=3.6.0 <4.0.0"
dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0"