import 'dart:ffi'; import 'dart:isolate'; import 'package:cw_monero/api/account_list.dart'; 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:ffi/ffi.dart'; import 'package:monero/monero.dart' as monero; import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen; String getTxKey(String txId) { return monero.Wallet_getTxKey(wptr!, txid: txId); } monero.TransactionHistory? txhistory; void refreshTransactions() { txhistory = monero.Wallet_history(wptr!); monero.TransactionHistory_refresh(txhistory!); } int countOfTransactions() => monero.TransactionHistory_count(txhistory!); List getAllTransactions() { final size = countOfTransactions(); return List.generate(size, (index) => Transaction(txInfo: monero.TransactionHistory_transaction(txhistory!, index: index))); } // TODO(mrcyjanek): ... Transaction getTransaction(String txId) { return Transaction(txInfo: monero.TransactionHistory_transactionById(txhistory!, txid: txId)); } Future createTransactionSync( {required String address, required String paymentId, required int priorityRaw, String? amount, int accountIndex = 0, List preferredInputs = const []}) async { final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount); final address_ = address.toNativeUtf8(); final paymentId_ = paymentId.toNativeUtf8(); final preferredInputs_ = preferredInputs.join(monero.defaultSeparatorStr).toNativeUtf8(); final waddr = wptr!.address; final addraddr = address_.address; final paymentIdAddr = paymentId_.address; final preferredInputsAddr = preferredInputs_.address; final spaddr = monero.defaultSeparator.address; final pendingTx = Pointer.fromAddress(await Isolate.run(() { final tx = monero_gen.MoneroC(DynamicLibrary.open(monero.libPath)).MONERO_Wallet_createTransaction( Pointer.fromAddress(waddr), Pointer.fromAddress(addraddr).cast(), Pointer.fromAddress(paymentIdAddr).cast(), amt, 1, priorityRaw, accountIndex, Pointer.fromAddress(preferredInputsAddr).cast(), Pointer.fromAddress(spaddr), ); return tx.address; })); calloc.free(address_); calloc.free(paymentId_); calloc.free(preferredInputs_); final String? error = (() { final status = monero.PendingTransaction_status(pendingTx); if (status == 0) { return null; } return monero.PendingTransaction_errorString(pendingTx); })(); if (error != null) { final message = error; throw CreationTransactionException(message: message); } final rAmt = monero.PendingTransaction_amount(pendingTx); final rFee = monero.PendingTransaction_fee(pendingTx); final rHash = monero.PendingTransaction_txid(pendingTx, ''); final rTxKey = rHash; return PendingTransactionDescription( amount: rAmt, fee: rFee, hash: rHash, hex: '', txKey: rTxKey, pointerAddress: pendingTx.address, ); } PendingTransactionDescription createTransactionMultDestSync( {required List outputs, required String paymentId, required int priorityRaw, int accountIndex = 0, List preferredInputs = const []}) { final txptr = monero.Wallet_createTransactionMultDest( wptr!, dstAddr: outputs.map((e) => e.address).toList(), isSweepAll: false, amounts: outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList(), mixinCount: 0, pendingTransactionPriority: priorityRaw, subaddr_account: accountIndex, ); if (monero.PendingTransaction_status(txptr) != 0) { throw CreationTransactionException(message: monero.PendingTransaction_errorString(txptr)); } return PendingTransactionDescription( amount: monero.PendingTransaction_amount(txptr), fee: monero.PendingTransaction_fee(txptr), hash: monero.PendingTransaction_txid(txptr, ''), hex: monero.PendingTransaction_txid(txptr, ''), txKey: monero.PendingTransaction_txid(txptr, ''), pointerAddress: txptr.address, ); } void commitTransactionFromPointerAddress({required int address}) => commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address)); void commitTransaction({required monero.PendingTransaction transactionPointer}) { final txCommit = monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false); final status = monero.PendingTransaction_status(transactionPointer.cast()); final String? error = (() { final status = monero.Wallet_status(wptr!); if (status == 0) { return null; } return monero.Wallet_errorString(wptr!); })(); if (error != null) { throw CreationTransactionException(message: error); } } Future _createTransactionSync(Map args) async { final address = args['address'] as String; final paymentId = args['paymentId'] as String; final amount = args['amount'] as String?; final priorityRaw = args['priorityRaw'] as int; final accountIndex = args['accountIndex'] as int; final preferredInputs = args['preferredInputs'] as List; return createTransactionSync( address: address, paymentId: paymentId, amount: amount, priorityRaw: priorityRaw, accountIndex: accountIndex, preferredInputs: preferredInputs); } PendingTransactionDescription _createTransactionMultDestSync(Map args) { final outputs = args['outputs'] as List; final paymentId = args['paymentId'] as String; final priorityRaw = args['priorityRaw'] as int; final accountIndex = args['accountIndex'] as int; final preferredInputs = args['preferredInputs'] as List; return createTransactionMultDestSync( outputs: outputs, paymentId: paymentId, priorityRaw: priorityRaw, accountIndex: accountIndex, preferredInputs: preferredInputs); } Future createTransaction( {required String address, required int priorityRaw, String? amount, String paymentId = '', int accountIndex = 0, List preferredInputs = const []}) async => _createTransactionSync({ 'address': address, 'paymentId': paymentId, 'amount': amount, 'priorityRaw': priorityRaw, 'accountIndex': accountIndex, 'preferredInputs': preferredInputs }); Future createTransactionMultDest( {required List outputs, required int priorityRaw, String paymentId = '', int accountIndex = 0, List preferredInputs = const []}) async => _createTransactionMultDestSync({ 'outputs': outputs, 'paymentId': paymentId, 'priorityRaw': priorityRaw, 'accountIndex': accountIndex, 'preferredInputs': preferredInputs }); class Transaction { final String displayLabel; String subaddressLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0); late final String address = monero.Wallet_address( wptr!, accountIndex: 0, addressIndex: 0, ); final String description; final int fee; final int confirmations; late final bool isPending = confirmations < 10; final int blockheight; final int addressIndex = 0; final int accountIndex; final String paymentId; final int amount; final bool isSpend; late DateTime timeStamp; late final bool isConfirmed = !isPending; final String hash; final String key; Map toJson() { return { "displayLabel": displayLabel, "subaddressLabel": subaddressLabel, "address": address, "description": description, "fee": fee, "confirmations": confirmations, "isPending": isPending, "blockheight": blockheight, "accountIndex": accountIndex, "addressIndex": addressIndex, "paymentId": paymentId, "amount": amount, "isSpend": isSpend, "timeStamp": timeStamp.toIso8601String(), "isConfirmed": isConfirmed, "hash": hash, }; } // S finalubAddress? subAddress; // List transfers = []; // final int txIndex; final monero.TransactionInfo txInfo; Transaction({ required this.txInfo, }) : displayLabel = monero.TransactionInfo_label(txInfo), hash = monero.TransactionInfo_hash(txInfo), timeStamp = DateTime.fromMillisecondsSinceEpoch( monero.TransactionInfo_timestamp(txInfo) * 1000, ), isSpend = monero.TransactionInfo_direction(txInfo) == monero.TransactionInfo_Direction.Out, amount = monero.TransactionInfo_amount(txInfo), paymentId = monero.TransactionInfo_paymentId(txInfo), accountIndex = monero.TransactionInfo_subaddrAccount(txInfo), blockheight = monero.TransactionInfo_blockHeight(txInfo), confirmations = monero.TransactionInfo_confirmations(txInfo), fee = monero.TransactionInfo_fee(txInfo), description = monero.TransactionInfo_description(txInfo), key = monero.Wallet_getTxKey(wptr!, txid: monero.TransactionInfo_hash(txInfo)); }