mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
Xelis wallet service rework, Xelis TX view formatting adjustments, Xelis event sub cleanup
This commit is contained in:
parent
9c679204c7
commit
18d94a04c7
12 changed files with 342 additions and 232 deletions
|
@ -1164,4 +1164,4 @@ packages:
|
||||||
version: "2.2.2"
|
version: "2.2.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0-0 <4.0.0"
|
dart: ">=3.7.0-0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.27.0"
|
||||||
|
|
|
@ -810,4 +810,4 @@ packages:
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0-0 <4.0.0"
|
dart: ">=3.7.0-0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.27.0"
|
||||||
|
|
|
@ -959,4 +959,4 @@ packages:
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0-0 <4.0.0"
|
dart: ">=3.7.0-0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.27.0"
|
||||||
|
|
|
@ -13,10 +13,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: asn1lib
|
name: asn1lib
|
||||||
sha256: "1c296cd268f486cabcc3930e9b93a8133169305f18d722916e675959a88f6d2c"
|
sha256: "0511d6be23b007e95105ae023db599aea731df604608978dada7f9faf2637623"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.9"
|
version: "1.6.4"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -157,10 +157,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -199,10 +199,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_rust_bridge
|
name: flutter_rust_bridge
|
||||||
sha256: b416ff56002789e636244fb4cc449f587656eff995e5a7169457eb0593fcaddb
|
sha256: "5a5c7a5deeef2cc2ffe6076a33b0429f4a20ceac22a397297aed2b1eb067e611"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.0"
|
version: "2.9.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -396,7 +396,7 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
resolved-ref: "096865a8c6b89c260beadfec04f7e184c40a3273"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "3.7.0"
|
version: "3.7.0"
|
||||||
|
@ -657,10 +657,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: unorm_dart
|
name: unorm_dart
|
||||||
sha256: "23d8bf65605401a6a32cff99435fed66ef3dab3ddcad3454059165df46496a3b"
|
sha256: "8e3870a1caa60bde8352f9597dd3535d8068613269444f8e35ea8925ec84c1f5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.3.1+1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -745,11 +745,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "60f9aa38d5e31256ac13e9d23108ce2776fe9cb1"
|
ref: "83bda92f1b833fe5d8584aa429d5143a3698b33f"
|
||||||
resolved-ref: "60f9aa38d5e31256ac13e9d23108ce2776fe9cb1"
|
resolved-ref: "83bda92f1b833fe5d8584aa429d5143a3698b33f"
|
||||||
url: "https://github.com/xelis-project/xelis-flutter-ffi.git"
|
url: "https://github.com/xelis-project/xelis-flutter-ffi.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0-0 <4.0.0"
|
dart: ">=3.7.0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.27.0"
|
||||||
|
|
|
@ -8,8 +8,6 @@ extension NetworkName on Network {
|
||||||
return 'mainnet';
|
return 'mainnet';
|
||||||
case Network.testnet:
|
case Network.testnet:
|
||||||
return 'testnet';
|
return 'testnet';
|
||||||
case Network.dev:
|
|
||||||
return 'dev';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +17,6 @@ extension NetworkName on Network {
|
||||||
return Network.mainnet;
|
return Network.mainnet;
|
||||||
case 'testnet':
|
case 'testnet':
|
||||||
return Network.testnet;
|
return Network.testnet;
|
||||||
case 'dev':
|
|
||||||
return Network.dev;
|
|
||||||
default:
|
default:
|
||||||
throw ArgumentError('Unknown network name: $name');
|
throw ArgumentError('Unknown network name: $name');
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ class XelisTransactionInfo extends TransactionInfo {
|
||||||
assetAmounts[asset] = (assetAmounts[asset] ?? BigInt.zero) + BigInt.from(transfer.amount);
|
assetAmounts[asset] = (assetAmounts[asset] ?? BigInt.zero) + BigInt.from(transfer.amount);
|
||||||
|
|
||||||
if (txType.transfers.length > 1) {
|
if (txType.transfers.length > 1) {
|
||||||
formattedTransfers.add("${transfer.destination} ( $formatted )");
|
formattedTransfers.add("${transfer.destination} [ $formatted ]");
|
||||||
} else {
|
} else {
|
||||||
formattedTransfers.add("${transfer.destination}");
|
formattedTransfers.add("${transfer.destination}");
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ class XelisTransactionInfo extends TransactionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
fee = BigInt.from(txType.fee);
|
fee = BigInt.from(txType.fee);
|
||||||
to = "SCID:\n${txType.contract}\n\nchunk_id ${txType.chunkId}";
|
to = "SCID:\n${txType.contract}\n\nChunk ID:\n${txType.chunkId}";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case xelis_sdk.DeployContractEntry():
|
case xelis_sdk.DeployContractEntry():
|
||||||
|
|
|
@ -35,6 +35,7 @@ import 'package:cw_xelis/xelis_store_utils.dart';
|
||||||
import 'package:cw_core/wallet_keys_file.dart';
|
import 'package:cw_core/wallet_keys_file.dart';
|
||||||
|
|
||||||
import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk;
|
import 'package:xelis_dart_sdk/xelis_dart_sdk.dart' as xelis_sdk;
|
||||||
|
import 'package:mutex/mutex.dart';
|
||||||
|
|
||||||
part 'xelis_wallet.g.dart';
|
part 'xelis_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -190,7 +191,8 @@ abstract class XelisWalletBase
|
||||||
bool _isTransactionUpdating;
|
bool _isTransactionUpdating;
|
||||||
StreamSubscription<void>? _eventSub;
|
StreamSubscription<void>? _eventSub;
|
||||||
|
|
||||||
void _subscribeToWalletEvents() {
|
Future<void> _subscribeToWalletEvents() async {
|
||||||
|
await _unsubscribeFromWalletEvents();
|
||||||
_eventSub = _convertRawEvents().listen(_handleEvent);
|
_eventSub = _convertRawEvents().listen(_handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +203,6 @@ abstract class XelisWalletBase
|
||||||
|
|
||||||
Stream<Event> _convertRawEvents() async* {
|
Stream<Event> _convertRawEvents() async* {
|
||||||
final stream = _libWallet.eventsStream();
|
final stream = _libWallet.eventsStream();
|
||||||
printV("eventsStream init");
|
|
||||||
|
|
||||||
await for (final raw in stream) {
|
await for (final raw in stream) {
|
||||||
try {
|
try {
|
||||||
|
@ -299,12 +300,14 @@ abstract class XelisWalletBase
|
||||||
}
|
}
|
||||||
connecting = true;
|
connecting = true;
|
||||||
try {
|
try {
|
||||||
_subscribeToWalletEvents();
|
await _subscribeToWalletEvents();
|
||||||
String addr = isTestnet ? "testnet-node.xelis.io" : "us-node.xelis.io";
|
String addr = isTestnet ? "testnet-node.xelis.io" : "us-node.xelis.io";
|
||||||
if (node.uri.host != addr) {
|
if (node.uri.host != addr) {
|
||||||
addr = node.uri.host;
|
addr = node.uri.host;
|
||||||
if (node.uri.port != "") {
|
if (node.uri.port != null && node.uri.port > 0) {
|
||||||
addr += ":" + node.uri.port.toString();
|
addr += ":${node.uri.port}";
|
||||||
|
} else {
|
||||||
|
addr += ":443";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (addr != persistantPeer) {
|
if (addr != persistantPeer) {
|
||||||
|
@ -347,53 +350,56 @@ abstract class XelisWalletBase
|
||||||
Future<void> rescan({required int height}) async {
|
Future<void> rescan({required int height}) async {
|
||||||
walletInfo.restoreHeight = height;
|
walletInfo.restoreHeight = height;
|
||||||
walletInfo.isRecovery = true;
|
walletInfo.isRecovery = true;
|
||||||
syncStatus = AttemptingSyncStatus();
|
|
||||||
balance.clear();
|
balance.clear();
|
||||||
|
|
||||||
final curr = isTestnet ? CryptoCurrency.xet : CryptoCurrency.xel;
|
final curr = isTestnet ? CryptoCurrency.xet : CryptoCurrency.xel;
|
||||||
balance[curr] = XelisAssetBalance.zero(symbol: isTestnet ? "XET" : "XEL");
|
balance[curr] = XelisAssetBalance.zero(symbol: isTestnet ? "XET" : "XEL");
|
||||||
|
|
||||||
await _libWallet.rescan(topoheight: BigInt.from(pruneHeight > height ? pruneHeight : height));
|
await _libWallet.rescan(topoheight: BigInt.from(pruneHeight > height ? pruneHeight : height));
|
||||||
|
syncStatus = NotConnectedSyncStatus();
|
||||||
|
await connectToNode(node: currentNode!);
|
||||||
await walletInfo.save();
|
await walletInfo.save();
|
||||||
syncStatus = SyncedSyncStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<XelisTransactionInfo> _txBuffer = [];
|
List<XelisTransactionInfo> _txBuffer = [];
|
||||||
Timer? _txBatchTimer;
|
Timer? _txBatchTimer;
|
||||||
Timer? _txSaveDebounceTimer;
|
Timer? _txSaveDebounceTimer;
|
||||||
|
final _txBufferLock = Mutex();
|
||||||
|
|
||||||
void _bufferTransaction(XelisTransactionInfo tx) {
|
void _bufferTransaction(XelisTransactionInfo tx) {
|
||||||
|
_txBufferLock.protect(() async {
|
||||||
_txBuffer.add(tx);
|
_txBuffer.add(tx);
|
||||||
|
|
||||||
if (_txBatchTimer == null || !_txBatchTimer!.isActive) {
|
if (_txBuffer.length > 1000) {
|
||||||
_txBatchTimer = Timer(Duration(seconds: 1), () async {
|
_txBatchTimer?.cancel();
|
||||||
final buffered = List<XelisTransactionInfo>.from(_txBuffer);
|
_txBatchTimer = Timer(Duration.zero, _processTransactionBuffer);
|
||||||
_txBuffer.clear();
|
} else if (_txBatchTimer == null || !_txBatchTimer!.isActive) {
|
||||||
_txBatchTimer = null;
|
_txBatchTimer = Timer(Duration(seconds: 1), _processTransactionBuffer);
|
||||||
|
}
|
||||||
final txMap = {
|
|
||||||
for (var tx in buffered) tx.id: tx
|
|
||||||
};
|
|
||||||
transactionHistory.addMany(txMap);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _flushTransactionBuffer() async {
|
Future<void> _processTransactionBuffer() async {
|
||||||
|
await _txBufferLock.protect(() async {
|
||||||
if (_txBuffer.isEmpty) return;
|
if (_txBuffer.isEmpty) return;
|
||||||
|
|
||||||
final toAdd = Map.fromEntries(_txBuffer.map((tx) => MapEntry(tx.id, tx)));
|
final buffered = List<XelisTransactionInfo>.from(_txBuffer);
|
||||||
|
|
||||||
runInAction(() {
|
|
||||||
transactionHistory.addMany(toAdd);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
final txMap = {for (var tx in buffered) tx.id: tx};
|
||||||
|
runInAction(() => transactionHistory.addMany(txMap));
|
||||||
_txBuffer.clear();
|
_txBuffer.clear();
|
||||||
_txBatchTimer = null;
|
|
||||||
|
|
||||||
|
_txSaveDebounceTimer?.cancel();
|
||||||
|
_txSaveDebounceTimer = Timer(Duration(seconds: 2), () async {
|
||||||
|
await transactionHistory.save();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> _handleEvent(Event event) async {
|
Future<void> _handleEvent(Event event) async {
|
||||||
|
try {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case NewTransaction():
|
case NewTransaction():
|
||||||
if (!isSupportedEntryType(event.tx)) break;
|
if (!isSupportedEntryType(event.tx)) break;
|
||||||
|
@ -404,13 +410,6 @@ abstract class XelisWalletBase
|
||||||
isAssetEnabled: (id) => findTrackedAssetById(id)?.enabled ?? id == xelis_sdk.xelisAsset
|
isAssetEnabled: (id) => findTrackedAssetById(id)?.enabled ?? id == xelis_sdk.xelisAsset
|
||||||
);
|
);
|
||||||
_bufferTransaction(transactionInfo);
|
_bufferTransaction(transactionInfo);
|
||||||
|
|
||||||
_txSaveDebounceTimer?.cancel();
|
|
||||||
_txSaveDebounceTimer = Timer(Duration(seconds: 1), () async {
|
|
||||||
await _flushTransactionBuffer();
|
|
||||||
await transactionHistory.save();
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BalanceChanged():
|
case BalanceChanged():
|
||||||
|
@ -499,6 +498,9 @@ abstract class XelisWalletBase
|
||||||
// _lastSyncError = event.message;
|
// _lastSyncError = event.message;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
printV("Error handling event $event: $e\n$s");
|
||||||
|
}
|
||||||
await Future.delayed(Duration.zero);
|
await Future.delayed(Duration.zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,6 +552,7 @@ abstract class XelisWalletBase
|
||||||
|
|
||||||
Future<void> _fetchAssetBalances() async {
|
Future<void> _fetchAssetBalances() async {
|
||||||
for (final asset in xelAssetsBox.values) {
|
for (final asset in xelAssetsBox.values) {
|
||||||
|
try {
|
||||||
if (asset.id == xelis_sdk.xelisAsset) continue;
|
if (asset.id == xelis_sdk.xelisAsset) continue;
|
||||||
|
|
||||||
final isTracked = await _libWallet.isAssetTracked(asset: asset.id);
|
final isTracked = await _libWallet.isAssetTracked(asset: asset.id);
|
||||||
|
@ -559,6 +562,9 @@ abstract class XelisWalletBase
|
||||||
} else if (!asset.enabled && isTracked) {
|
} else if (!asset.enabled && isTracked) {
|
||||||
await _libWallet.untrackAsset(asset: asset.id);
|
await _libWallet.untrackAsset(asset: asset.id);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
printV("Failed to sync tracking for asset ${asset.id}: $e");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final bal = await _libWallet.getTrackedAssetBalancesRaw();
|
final bal = await _libWallet.getTrackedAssetBalancesRaw();
|
||||||
|
@ -1028,6 +1034,14 @@ abstract class XelisWalletBase
|
||||||
_isTransactionUpdating = false;
|
_isTransactionUpdating = false;
|
||||||
_txSaveDebounceTimer?.cancel();
|
_txSaveDebounceTimer?.cancel();
|
||||||
_txBatchTimer?.cancel();
|
_txBatchTimer?.cancel();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _processTransactionBuffer();
|
||||||
|
await transactionHistory.save();
|
||||||
|
} catch (e) {
|
||||||
|
printV("Error during wallet close cleanup: $e");
|
||||||
|
}
|
||||||
|
|
||||||
await _unsubscribeFromWalletEvents();
|
await _unsubscribeFromWalletEvents();
|
||||||
await _libWallet.close();
|
await _libWallet.close();
|
||||||
x_wallet.dropWallet(wallet: _libWallet);
|
x_wallet.dropWallet(wallet: _libWallet);
|
||||||
|
|
|
@ -42,6 +42,67 @@ class MemoryTierCalculator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class XelisLoggerFactory {
|
||||||
|
static bool _isSetup = false;
|
||||||
|
static const LOG_LEVEL = 3;
|
||||||
|
/*
|
||||||
|
Log level for FFI Rust outputs in xelis_flutter
|
||||||
|
|
||||||
|
0: None
|
||||||
|
1: Error
|
||||||
|
2: Warn
|
||||||
|
3: Info
|
||||||
|
4: Debug
|
||||||
|
5: Trace
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
static Future<void> setupIfNeeded() async {
|
||||||
|
if (_isSetup) return;
|
||||||
|
await x_api.setUpRustLogger();
|
||||||
|
_setupLogStream();
|
||||||
|
_isSetup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _setupLogStream() {
|
||||||
|
x_api.createLogStream().listen((entry) {
|
||||||
|
final logLine = 'XELIS LOG | [${entry.level.name}] ${entry.tag}: ${entry.msg}';
|
||||||
|
|
||||||
|
switch (entry.level) {
|
||||||
|
case x_logger.Level.error:
|
||||||
|
if (LOG_LEVEL > 0) {
|
||||||
|
printV('❌ $logLine');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case x_logger.Level.warn:
|
||||||
|
if (LOG_LEVEL > 1) {
|
||||||
|
printV('⚠️ $logLine');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case x_logger.Level.info:
|
||||||
|
if (LOG_LEVEL > 2) {
|
||||||
|
printV('ℹ️ $logLine');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case x_logger.Level.debug:
|
||||||
|
if (LOG_LEVEL > 3) {
|
||||||
|
printV('🐛 $logLine');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case x_logger.Level.trace:
|
||||||
|
if (LOG_LEVEL > 4) {
|
||||||
|
printV('🔍 $logLine');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (dynamic e) {
|
||||||
|
printV("Error receiving Xelis Rust logs: $e");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum XelisTableSize {
|
enum XelisTableSize {
|
||||||
initial,
|
initial,
|
||||||
web,
|
web,
|
||||||
|
@ -142,20 +203,8 @@ class XelisWalletService extends WalletService<
|
||||||
XelisNewWalletCredentials
|
XelisNewWalletCredentials
|
||||||
> {
|
> {
|
||||||
XelisWalletService(this.walletInfoSource, {required this.isDirect}) {
|
XelisWalletService(this.walletInfoSource, {required this.isDirect}) {
|
||||||
setupRustLogger();
|
XelisLoggerFactory.setupIfNeeded();
|
||||||
}
|
}
|
||||||
static const LOG_LEVEL = 3;
|
|
||||||
/*
|
|
||||||
Log level for FFI Rust outputs in xelis_flutter
|
|
||||||
|
|
||||||
0: None
|
|
||||||
1: Error
|
|
||||||
2: Warn
|
|
||||||
3: Info
|
|
||||||
4: Debug
|
|
||||||
5: Trace
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
final bool isDirect;
|
final bool isDirect;
|
||||||
|
@ -165,45 +214,6 @@ class XelisWalletService extends WalletService<
|
||||||
static Completer<void>? _tableUpgradeCompleter;
|
static Completer<void>? _tableUpgradeCompleter;
|
||||||
static XelisWallet? _activeWallet;
|
static XelisWallet? _activeWallet;
|
||||||
|
|
||||||
void setupRustLogger() async {
|
|
||||||
await x_api.setUpRustLogger();
|
|
||||||
|
|
||||||
x_api.createLogStream().listen((entry) {
|
|
||||||
final logLine = 'XELIS LOG | [${entry.level.name}] ${entry.tag}: ${entry.msg}';
|
|
||||||
|
|
||||||
switch (entry.level) {
|
|
||||||
case x_logger.Level.error:
|
|
||||||
if (LOG_LEVEL > 0) {
|
|
||||||
printV('❌ $logLine');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case x_logger.Level.warn:
|
|
||||||
if (LOG_LEVEL > 1) {
|
|
||||||
printV('⚠️ $logLine');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case x_logger.Level.info:
|
|
||||||
if (LOG_LEVEL > 2) {
|
|
||||||
printV('ℹ️ $logLine');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case x_logger.Level.debug:
|
|
||||||
if (LOG_LEVEL > 3) {
|
|
||||||
printV('🐛 $logLine');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case x_logger.Level.trace:
|
|
||||||
if (LOG_LEVEL > 4) {
|
|
||||||
printV('🔍 $logLine');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: (dynamic e) {
|
|
||||||
printV("Error receiving Xelis Rust logs: $e");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletType getType() => WalletType.xelis;
|
WalletType getType() => WalletType.xelis;
|
||||||
|
|
||||||
|
@ -216,7 +226,7 @@ class XelisWalletService extends WalletService<
|
||||||
try {
|
try {
|
||||||
await _activeWallet!.close();
|
await _activeWallet!.close();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
printV("Error closing active Xelis wallet: $e");
|
||||||
}
|
}
|
||||||
_activeWallet = null;
|
_activeWallet = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ dependencies:
|
||||||
xelis_flutter:
|
xelis_flutter:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/xelis-project/xelis-flutter-ffi.git
|
url: https://github.com/xelis-project/xelis-flutter-ffi.git
|
||||||
ref: 60f9aa38d5e31256ac13e9d23108ce2776fe9cb1
|
ref: 83bda92f1b833fe5d8584aa429d5143a3698b33f
|
||||||
# path: ./xelis-flutter-ffi
|
# path: ./xelis-flutter-ffi
|
||||||
xelis_dart_sdk: ^0.28.0
|
xelis_dart_sdk: ^0.28.0
|
||||||
mutex: ^3.1.0
|
mutex: ^3.1.0
|
||||||
|
|
|
@ -59,7 +59,7 @@ class WalletUnlockPageState extends AuthPageState<WalletUnlockPage> {
|
||||||
if (state is IsLoadingState) {
|
if (state is IsLoadingState) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
// null duration to make it indefinite until its disposed
|
// null duration to make it indefinite until its disposed
|
||||||
_authBar = createBar<void>(S.of(context).loading_wallet, duration: null)..show(context);
|
_authBar = createBar<void>(S.of(context).loading_wallet, context, duration: null)..show(context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,103 @@ class AddressFormatter {
|
||||||
TextAlign? textAlign,
|
TextAlign? textAlign,
|
||||||
bool shouldTruncate = false,
|
bool shouldTruncate = false,
|
||||||
}) {
|
}) {
|
||||||
|
// Check for parentheses in the address
|
||||||
|
final bracketIndex = address.indexOf('[');
|
||||||
|
|
||||||
|
if (bracketIndex != -1) {
|
||||||
|
// Split address and amount parts
|
||||||
|
final addressPart = address.substring(0, bracketIndex).trim();
|
||||||
|
final amountPart = address.substring(bracketIndex);
|
||||||
|
|
||||||
|
// For truncated addresses, handle differently
|
||||||
|
if (shouldTruncate) {
|
||||||
|
final addressWidget = _buildAddressWidget(
|
||||||
|
address: addressPart,
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: evenTextStyle,
|
||||||
|
oddTextStyle: oddTextStyle,
|
||||||
|
textAlign: textAlign,
|
||||||
|
shouldTruncate: shouldTruncate,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
addressWidget,
|
||||||
|
Text(amountPart, style: evenTextStyle),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For full addresses, integrate amount with last line
|
||||||
|
final cleanAddress = addressPart.replaceAll('bitcoincash:', '');
|
||||||
|
final isMWEB = addressPart.startsWith('ltcmweb');
|
||||||
|
final chunkSize = walletType != null ? _getChunkSize(walletType) : 4;
|
||||||
|
|
||||||
|
// Build chunks
|
||||||
|
final chunks = <String>[];
|
||||||
|
if (isMWEB) {
|
||||||
|
const mwebDisplayPrefix = 'ltcmweb';
|
||||||
|
chunks.add(mwebDisplayPrefix);
|
||||||
|
final startIndex = mwebDisplayPrefix.length;
|
||||||
|
for (int i = startIndex; i < cleanAddress.length; i += chunkSize) {
|
||||||
|
final chunk = cleanAddress.substring(
|
||||||
|
i,
|
||||||
|
math.min(i + chunkSize, cleanAddress.length),
|
||||||
|
);
|
||||||
|
chunks.add(chunk);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < cleanAddress.length; i += chunkSize) {
|
||||||
|
final chunk = cleanAddress.substring(
|
||||||
|
i,
|
||||||
|
math.min(i + chunkSize, cleanAddress.length),
|
||||||
|
);
|
||||||
|
chunks.add(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build text spans with amount appended to last chunk
|
||||||
|
final spans = <TextSpan>[];
|
||||||
|
for (int i = 0; i < chunks.length; i++) {
|
||||||
|
final style = (i % 2 == 0) ? evenTextStyle : oddTextStyle ?? evenTextStyle.copyWith(color: evenTextStyle.color!.withAlpha(128));
|
||||||
|
|
||||||
|
if (i == chunks.length - 1) {
|
||||||
|
// Last chunk - append amount
|
||||||
|
spans.add(TextSpan(text: '${chunks[i]} ', style: style));
|
||||||
|
spans.add(TextSpan(text: amountPart, style: evenTextStyle));
|
||||||
|
} else {
|
||||||
|
spans.add(TextSpan(text: '${chunks[i]} ', style: style));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RichText(
|
||||||
|
text: TextSpan(children: spans),
|
||||||
|
textAlign: textAlign ?? TextAlign.start,
|
||||||
|
overflow: TextOverflow.visible,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No parentheses - use original logic
|
||||||
|
return _buildAddressWidget(
|
||||||
|
address: address,
|
||||||
|
walletType: walletType,
|
||||||
|
evenTextStyle: evenTextStyle,
|
||||||
|
oddTextStyle: oddTextStyle,
|
||||||
|
textAlign: textAlign,
|
||||||
|
shouldTruncate: shouldTruncate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildAddressWidget({
|
||||||
|
required String address,
|
||||||
|
WalletType? walletType,
|
||||||
|
required TextStyle evenTextStyle,
|
||||||
|
TextStyle? oddTextStyle,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
bool shouldTruncate = false,
|
||||||
|
}) {
|
||||||
final cleanAddress = address.replaceAll('bitcoincash:', '');
|
final cleanAddress = address.replaceAll('bitcoincash:', '');
|
||||||
final isMWEB = address.startsWith('ltcmweb');
|
final isMWEB = address.startsWith('ltcmweb');
|
||||||
final chunkSize = walletType != null ? _getChunkSize(walletType) : 4;
|
final chunkSize = walletType != null ? _getChunkSize(walletType) : 4;
|
||||||
|
|
|
@ -560,6 +560,24 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
throw Exception("Pending transaction doesn't exist. It should not be happened.");
|
throw Exception("Pending transaction doesn't exist. It should not be happened.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ocpRequest != null) {
|
||||||
|
state = TransactionCommitting();
|
||||||
|
if (selectedCryptoCurrency == CryptoCurrency.xmr) {
|
||||||
|
await pendingTransaction!.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _ocpService.commitOpenCryptoPayRequest(
|
||||||
|
pendingTransaction!.hex,
|
||||||
|
txId: pendingTransaction!.id,
|
||||||
|
request: ocpRequest!,
|
||||||
|
asset: selectedCryptoCurrency,
|
||||||
|
);
|
||||||
|
|
||||||
|
state = TransactionCommitted();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
late String address;
|
late String address;
|
||||||
|
|
||||||
if (walletType == WalletType.xelis) {
|
if (walletType == WalletType.xelis) {
|
||||||
|
@ -580,30 +598,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ocpRequest != null) {
|
|
||||||
state = TransactionCommitting();
|
|
||||||
if (selectedCryptoCurrency == CryptoCurrency.xmr) {
|
|
||||||
await pendingTransaction!.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
await _ocpService.commitOpenCryptoPayRequest(
|
|
||||||
pendingTransaction!.hex,
|
|
||||||
txId: pendingTransaction!.id,
|
|
||||||
request: ocpRequest!,
|
|
||||||
asset: selectedCryptoCurrency,
|
|
||||||
);
|
|
||||||
|
|
||||||
state = TransactionCommitted();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String address = outputs.fold('', (acc, value) {
|
|
||||||
return value.isParsedAddress
|
|
||||||
? '$acc${value.address}\n${value.extractedAddress}\n\n'
|
|
||||||
: '$acc${value.address}\n\n';
|
|
||||||
});
|
|
||||||
|
|
||||||
address = address.trim();
|
address = address.trim();
|
||||||
|
|
||||||
String note = outputs.fold('', (acc, value) => '$acc${value.note}\n');
|
String note = outputs.fold('', (acc, value) => '$acc${value.note}\n');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue