import 'dart:ffi'; 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; 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)); } PendingTransactionDescription createTransactionSync( {required String address, required String paymentId, required int priorityRaw, String? amount, int accountIndex = 0, List preferredInputs = const []}) { final amountPointer = amount != null ? amount.toNativeUtf8() : nullptr; final int preferredInputsSize = preferredInputs.length; final List> preferredInputsPointers = preferredInputs.map((output) => output.toNativeUtf8()).toList(); final Pointer> preferredInputsPointerPointer = calloc(preferredInputsSize); for (int i = 0; i < preferredInputsSize; i++) { preferredInputsPointerPointer[i] = preferredInputsPointers[i]; } final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount); final pendingTx = monero.Wallet_createTransaction( wptr!, dst_addr: address, payment_id: paymentId, amount: amt, mixin_count: 1, pendingTransactionPriority: priorityRaw, subaddr_account: accountIndex, preferredInputs: preferredInputs, ); final String? error = (() { final status = monero.Wallet_status(wptr!); if (status == 0) { return null; } return monero.Wallet_errorString(wptr!); })(); if (error != null) { final message = error; throw CreationTransactionException(message: message); } return PendingTransactionDescription( amount: monero.PendingTransaction_amount(wptr!), fee: monero.PendingTransaction_fee(wptr!), hash: monero.PendingTransaction_txid(wptr!, ''), hex: '', txKey: monero.PendingTransaction_txid(wptr!, ''), pointerAddress: pendingTx.address, ); } PendingTransactionDescription createTransactionMultDestSync( {required List outputs, required String paymentId, required int priorityRaw, int accountIndex = 0, List preferredInputs = const []}) { // final int size = outputs.length; // final List> addressesPointers = // outputs.map((output) => output.address.toNativeUtf8()).toList(); // final Pointer> addressesPointerPointer = calloc(size); // final List> amountsPointers = // outputs.map((output) => output.amount.toNativeUtf8()).toList(); // final Pointer> amountsPointerPointer = calloc(size); // for (int i = 0; i < size; i++) { // addressesPointerPointer[i] = addressesPointers[i]; // amountsPointerPointer[i] = amountsPointers[i]; // } // final int preferredInputsSize = preferredInputs.length; // final List> preferredInputsPointers = // preferredInputs.map((output) => output.toNativeUtf8()).toList(); // final Pointer> preferredInputsPointerPointer = calloc(preferredInputsSize); // for (int i = 0; i < preferredInputsSize; i++) { // preferredInputsPointerPointer[i] = preferredInputsPointers[i]; // } // final paymentIdPointer = paymentId.toNativeUtf8(); // final errorMessagePointer = calloc(); // final pendingTransactionRawPointer = calloc(); // final created = transactionCreateMultDestNative( // addressesPointerPointer, // paymentIdPointer, // amountsPointerPointer, // size, // priorityRaw, // accountIndex, // preferredInputsPointerPointer, // preferredInputsSize, // errorMessagePointer, // pendingTransactionRawPointer) != // 0; // calloc.free(addressesPointerPointer); // calloc.free(amountsPointerPointer); // calloc.free(preferredInputsPointerPointer); // addressesPointers.forEach((element) => calloc.free(element)); // amountsPointers.forEach((element) => calloc.free(element)); // preferredInputsPointers.forEach((element) => calloc.free(element)); // calloc.free(paymentIdPointer); // if (!created) { // final message = errorMessagePointer.ref.getValue(); // calloc.free(errorMessagePointer); // throw CreationTransactionException(message: message); // } // return PendingTransactionDescription( // amount: pendingTransactionRawPointer.ref.amount, // fee: pendingTransactionRawPointer.ref.fee, // hash: pendingTransactionRawPointer.ref.getHash(), // hex: pendingTransactionRawPointer.ref.getHex(), // txKey: pendingTransactionRawPointer.ref.getKey(), // pointerAddress: pendingTransactionRawPointer.address); throw CreationTransactionException(message: "Unimplemented in monero_c"); } 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); } } PendingTransactionDescription _createTransactionSync(Map args) { 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 accountIndex; final String paymentId; final int amount; final bool isSpend; late DateTime timeStamp; late final bool isConfirmed = !isPending; final String hash; Map toJson() { return { "displayLabel": displayLabel, "subaddressLabel": subaddressLabel, "address": address, "description": description, "fee": fee, "confirmations": confirmations, "isPending": isPending, "blockheight": blockheight, "accountIndex": accountIndex, "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); }