diff --git a/dash-spv-ffi/src/callbacks.rs b/dash-spv-ffi/src/callbacks.rs index b7b300e5d..fc6a54568 100644 --- a/dash-spv-ffi/src/callbacks.rs +++ b/dash-spv-ffi/src/callbacks.rs @@ -731,6 +731,13 @@ impl FFIWalletEventCallbacks { .map(|d| FFIOutputDetail { index: d.index, role: FFIOutputRole::from(d.role), + value: d.value, + address: match &d.address { + Some(addr) => { + CString::new(addr.to_string()).unwrap_or_default().into_raw() + } + None => std::ptr::null_mut(), + }, }) .collect(); @@ -784,6 +791,14 @@ impl FFIWalletEventCallbacks { } } } + // Free the CString addresses from output details + for detail in output_details { + if !detail.address.is_null() { + unsafe { + drop(CString::from_raw(detail.address)); + } + } + } // SAFETY: Free the heap-allocated IS lock bytes produced by // `From` after the callback returns. if !islock_data.is_null() && islock_len > 0 { diff --git a/key-wallet-ffi/src/managed_account.rs b/key-wallet-ffi/src/managed_account.rs index fa91a46dd..77ca85902 100644 --- a/key-wallet-ffi/src/managed_account.rs +++ b/key-wallet-ffi/src/managed_account.rs @@ -791,6 +791,13 @@ pub unsafe extern "C" fn managed_core_account_get_transactions( .map(|d| FFIOutputDetail { index: d.index, role: FFIOutputRole::from(d.role), + value: d.value, + address: match &d.address { + Some(addr) => { + std::ffi::CString::new(addr.to_string()).unwrap_or_default().into_raw() + } + None => std::ptr::null_mut(), + }, }) .collect::>() .into_boxed_slice(); @@ -841,12 +848,16 @@ pub unsafe extern "C" fn managed_core_account_free_transactions( drop(Box::from_raw(slice as *mut [FFIInputDetail])); } - // Free output details + // Free output detail addresses first, then the array if !record.output_details.is_null() && record.output_details_count > 0 { - drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut( - record.output_details, - record.output_details_count, - ))); + let slice = + std::slice::from_raw_parts_mut(record.output_details, record.output_details_count); + for detail in slice.iter() { + if !detail.address.is_null() { + drop(std::ffi::CString::from_raw(detail.address)); + } + } + drop(Box::from_raw(slice as *mut [FFIOutputDetail])); } // Free tx data @@ -2027,7 +2038,7 @@ mod tests { let addr = std::ffi::CString::new("XtestAddress123").unwrap(); let input_slice = vec![FFIInputDetail { index: 0, - value: 100000, + value: 0, address: addr.into_raw(), }] .into_boxed_slice(); @@ -2038,6 +2049,8 @@ mod tests { let output_slice = vec![FFIOutputDetail { index: 0, role: FFIOutputRole::Received, + value: 0, + address: std::ptr::null_mut(), }] .into_boxed_slice(); r0.output_details_count = output_slice.len(); diff --git a/key-wallet-ffi/src/types.rs b/key-wallet-ffi/src/types.rs index 12495596b..71ee3ac08 100644 --- a/key-wallet-ffi/src/types.rs +++ b/key-wallet-ffi/src/types.rs @@ -930,6 +930,8 @@ pub struct FFIInputDetail { pub struct FFIOutputDetail { pub index: u32, pub role: FFIOutputRole, + pub value: u64, + pub address: *mut c_char, } #[cfg(test)] diff --git a/key-wallet/src/managed_account/mod.rs b/key-wallet/src/managed_account/mod.rs index 16a1c6539..f988e9189 100644 --- a/key-wallet/src/managed_account/mod.rs +++ b/key-wallet/src/managed_account/mod.rs @@ -504,6 +504,8 @@ impl ManagedCoreAccount { output_details.push(OutputDetail { index: idx as u32, role, + address: resolved_outputs[idx].clone(), + value: output.value, }); } diff --git a/key-wallet/src/managed_account/transaction_record.rs b/key-wallet/src/managed_account/transaction_record.rs index 478c1970e..e4eca2c36 100644 --- a/key-wallet/src/managed_account/transaction_record.rs +++ b/key-wallet/src/managed_account/transaction_record.rs @@ -37,6 +37,10 @@ pub struct OutputDetail { pub index: u32, /// Role of this output from the wallet's perspective pub role: OutputRole, + /// Decoded address (None for non-standard scripts) + pub address: Option
, + /// Value in satoshis + pub value: u64, } /// Role of a transaction output from the wallet's perspective