Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/taker_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Initial wallet state:");
println!(" Spendable: {} BTC", balances.spendable.to_btc());
println!(" Regular: {} BTC", balances.regular.to_btc());
println!(" UTXOs: {}", utxos.len());
println!(" UTXOs: {}", utxos.count());

// Fund the wallet if empty
if balances.spendable == Amount::ZERO {
Expand Down Expand Up @@ -173,7 +173,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" Contract: {} BTC", final_balances.contract.to_btc());

// Show UTXOs
let utxos = wallet_mut.list_all_utxo();
let utxos: Vec<_> = wallet_mut.list_all_utxo().collect();
println!("\nUTXO information:");
println!(" Total UTXOs: {}", utxos.len());
if !utxos.is_empty() {
Expand Down
10 changes: 5 additions & 5 deletions examples/wallet_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("\nUTXO Summary:");
let utxos = wallet.list_all_utxo();
let swapcoins_count = wallet.get_swapcoins_count();
println!(" Total UTXOs: {}", utxos.len());
println!(" Total UTXOs: {}", utxos.count());
println!(" Swap coins: {swapcoins_count}");

// Categorize UTXOs by type
Expand All @@ -160,7 +160,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let swept_utxos = wallet.list_swept_incoming_swap_utxos();
println!(" Regular UTXOs: {}", regular_utxos.len());
println!(" Swap UTXOs: {}", swap_utxos.len());
println!(" Fidelity UTXOs: {}", fidelity_utxos.len());
println!(" Fidelity UTXOs: {}", fidelity_utxos.count());
println!(" Swept swap UTXOs: {}", swept_utxos.len());

// Generate addresses
Expand Down Expand Up @@ -265,9 +265,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let hashlock_contracts = wallet.list_live_hashlock_contract_spend_info();
let all_utxo_info = wallet.list_all_utxo_spend_info();

println!(" Live contracts: {}", live_contracts.len());
println!(" Timelock contracts: {}", timelock_contracts.len());
println!(" Hashlock contracts: {}", hashlock_contracts.len());
println!(" Live contracts: {}", live_contracts.count());
println!(" Timelock contracts: {}", timelock_contracts.count());
println!(" Hashlock contracts: {}", hashlock_contracts.count());
println!(
" Detailed UTXO info available for {} UTXOs",
all_utxo_info.len()
Expand Down
4 changes: 2 additions & 2 deletions src/bin/taker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,14 @@ fn main() -> Result<(), TakerError> {
Commands::ListUtxoSwap => {
let utxos = taker.get_wallet().list_incoming_swap_coin_utxo_spend_info();
for utxo in utxos {
let utxo = UTXO::from_utxo_data(utxo);
let utxo = UTXO::from(utxo);
println!("{}", serde_json::to_string_pretty(&utxo)?);
}
}
Commands::ListUtxoContract => {
let utxos = taker.get_wallet().list_live_timelock_contract_spend_info();
for utxo in utxos {
let utxo = UTXO::from_utxo_data(utxo);
let utxo = UTXO::from(utxo);
println!("{}", serde_json::to_string_pretty(&utxo)?);
}
}
Expand Down
9 changes: 3 additions & 6 deletions src/maker/rpc/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ fn handle_request<M: MakerRpc>(maker: &Arc<M>, socket: &mut TcpStream) -> Result
.wallet()
.read()?
.list_live_timelock_contract_spend_info()
.into_iter()
.map(UTXO::from_utxo_data)
.map(UTXO::from)
.collect();
RpcMsgResp::ContractUtxoResp { utxos }
}
Expand All @@ -48,8 +47,7 @@ fn handle_request<M: MakerRpc>(maker: &Arc<M>, socket: &mut TcpStream) -> Result
.wallet()
.read()?
.list_fidelity_spend_info()
.into_iter()
.map(UTXO::from_utxo_data)
.map(UTXO::from)
.collect();
RpcMsgResp::FidelityUtxoResp { utxos }
}
Expand All @@ -68,8 +66,7 @@ fn handle_request<M: MakerRpc>(maker: &Arc<M>, socket: &mut TcpStream) -> Result
.wallet()
.read()?
.list_incoming_swap_coin_utxo_spend_info()
.into_iter()
.map(UTXO::from_utxo_data)
.map(UTXO::from)
.collect();
RpcMsgResp::SwapUtxoResp { utxos }
}
Expand Down
48 changes: 28 additions & 20 deletions src/taker/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ impl Taker {
/// If that fails too. Open an issue at [our github](https://github.com/citadel-tech/coinswap/issues)
pub(crate) fn send_coinswap(&mut self, swap_params: SwapParams) -> Result<(), TakerError> {
let swap_start_time = std::time::Instant::now();
let initial_utxoset = self.wallet.list_all_utxo();
self.ongoing_swap_state.swap_params = swap_params.clone();

// Check if we have enough balance - try regular first, then swap
Expand Down Expand Up @@ -518,7 +517,11 @@ impl Taker {

// Generate post-swap report
self.wallet.sync_and_save()?;
self.print_swap_report(prereset_swapstate, swap_start_time, initial_utxoset)?;
self.print_swap_report(
prereset_swapstate,
swap_start_time,
self.wallet.list_all_utxo(),
)?;
Comment on lines +520 to +524
Copy link

@mojoX911 mojoX911 Oct 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work. The initial utxo set passed on here previously was the set that existed before the swap. This will pass the utxo set after the swap. They are different. So this will mess up the report logic.

Revert back to previous way of capturing the initial (pre-swap) utxo set.

}
Err(e) => {
log::error!("Swap Settlement Failed : {e:?}");
Expand All @@ -531,11 +534,11 @@ impl Taker {
Ok(())
}

fn print_swap_report(
fn print_swap_report<'a>(
&self,
prereset_swapstate: &OngoingSwapState,
start_time: std::time::Instant,
initial_utxos: Vec<ListUnspentResultEntry>,
initial_utxos: impl Iterator<Item = &'a ListUnspentResultEntry>,
) -> Result<(), TakerError> {
let swap_state = &prereset_swapstate;

Expand All @@ -549,14 +552,6 @@ impl Taker {
.map(|(utxo, _)| utxo)
.collect::<Vec<_>>();

let initial_outpoints = initial_utxos
.iter()
.map(|utxo| OutPoint {
txid: utxo.txid,
vout: utxo.vout,
})
.collect::<HashSet<_>>();

let current_outpoints = all_regular_utxo
.iter()
.map(|utxo| OutPoint {
Expand All @@ -566,17 +561,30 @@ impl Taker {
.collect::<HashSet<_>>();

// Present in initial set but not in current set (destroyed)
let input_utxos = initial_utxos
.iter()
.filter(|utxo| {
let initial_outpoint = OutPoint {
let (initial_outpoints, input_utxos): (HashSet<_>, Vec<_>) = initial_utxos
.into_iter()
.map(|utxo| {
let outpoint = OutPoint {
Comment on lines +564 to +567

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try not to change logic not related to this PR, to reduce review surface. The previous way was just fine.

txid: utxo.txid,
vout: utxo.vout,
};
!current_outpoints.contains(&initial_outpoint)
let amount = if !current_outpoints.contains(&outpoint) {
Some(utxo.amount.to_sat())
} else {
None
};
(outpoint, amount)
})
.map(|utxo| utxo.amount.to_sat())
.collect::<Vec<u64>>();
.fold(
(HashSet::new(), Vec::new()),
|(mut set, mut vec), (outpoint, amount)| {
set.insert(outpoint);
if let Some(a) = amount {
vec.push(a);
}
(set, vec)
},
);

// Present in current set but not in initial regular set (created)
let output_regular_utxos = all_regular_utxo
Expand Down Expand Up @@ -698,7 +706,7 @@ impl Taker {
})
.sum::<f64>() as u64;

let mining_fee = total_fee - total_maker_fees;
let mining_fee = total_fee.saturating_sub(total_maker_fees);
println!("\n\x1b[1;37mMining Fees :\x1b[0m \x1b[36m{mining_fee} sats\x1b[0m",);
let fee_percentage = (total_fee as f64 / target_amount as f64) * 100.0;
println!("\x1b[1;37mTotal Fee Rate :\x1b[0m \x1b[1;31m{fee_percentage:.2} %\x1b[0m",);
Expand Down
17 changes: 17 additions & 0 deletions src/utill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,23 @@ impl UTXO {
}
}

impl From<(&ListUnspentResultEntry, &UTXOSpendInfo)> for UTXO {
fn from((utxo, spend_info): (&ListUnspentResultEntry, &UTXOSpendInfo)) -> Self {
let addr = utxo
.address
.clone()
.expect("address always expected")
.assume_checked()
.to_string();
Self {
addr,
amount: utxo.amount,
confirmations: utxo.confirmations,
utxo_type: spend_info.to_string(),
}
}
}

/// Compute the checksum of a descriptor
pub(crate) fn compute_checksum(descriptor: &str) -> Result<String, WalletError> {
let mut checksum = CHECKSUM_FINAL_XOR_VALUE;
Expand Down
99 changes: 43 additions & 56 deletions src/wallet/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,15 +410,13 @@ impl Wallet {
.fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount);
let contract = self
.list_live_timelock_contract_spend_info()
.iter()
.fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount);
let swap = self
.list_swept_incoming_swap_utxos()
.iter()
.fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount);
let fidelity = self
.list_fidelity_spend_info()
.iter()
.fold(Amount::ZERO, |sum, (utxo, _)| sum + utxo.amount);
let spendable = regular + swap;

Expand Down Expand Up @@ -736,11 +734,8 @@ impl Wallet {
}

/// Returns a list of all UTXOs tracked by the wallet. Including fidelity, live_contracts and swap coins.
pub fn list_all_utxo(&self) -> Vec<ListUnspentResultEntry> {
self.list_all_utxo_spend_info()
.iter()
.map(|(utxo, _)| utxo.clone())
.collect()
pub fn list_all_utxo(&self) -> impl Iterator<Item = &ListUnspentResultEntry> {
self.store.utxo_cache.values().map(|(utxo, _)| utxo)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to use list_all_utxo_spend_info to avoid repeating change later.

}

/// Returns a list all utxos with their spend info tracked by the wallet.
Expand All @@ -757,62 +752,58 @@ impl Wallet {
}

/// Lists live contract UTXOs along with their Spend info.
pub fn list_live_contract_spend_info(&self) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {
let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.filter(|x| {
matches!(x.1, UTXOSpendInfo::HashlockContract { .. })
|| matches!(x.1, UTXOSpendInfo::TimelockContract { .. })
pub fn list_live_contract_spend_info(
&self,
) -> impl Iterator<Item = (&ListUnspentResultEntry, &UTXOSpendInfo)> {
self.store
.utxo_cache
.values()
Comment on lines +758 to +760

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, use list_all_utxo_spend_info whenever possible.

If we keep the API pipeline same everywhere, then later if any changes needed, we can add it only to list_alll_utxo_spend_info without changing any other API.

.filter(|(_, spend_info)| {
matches!(spend_info, UTXOSpendInfo::HashlockContract { .. })
|| matches!(spend_info, UTXOSpendInfo::TimelockContract { .. })
})
.cloned()
.collect();
filtered_utxos
.map(|(utxo, spend_info)| (utxo, spend_info))
}

/// Lists live timelock contract UTXOs along with their Spend info.
pub fn list_live_timelock_contract_spend_info(
&self,
) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {
let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.filter(|x| matches!(x.1, UTXOSpendInfo::TimelockContract { .. }))
.cloned()
.collect();
filtered_utxos
) -> impl Iterator<Item = (&ListUnspentResultEntry, &UTXOSpendInfo)> + '_ {
self.store
.utxo_cache
.values()
.filter(|(_, spend)| matches!(spend, &UTXOSpendInfo::TimelockContract { .. }))
.map(|(utxo, spend)| (utxo, spend))
}

/// Lists all live hashlock contract UTXOs along with their Spend info.
pub fn list_live_hashlock_contract_spend_info(
&self,
) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {
let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.filter(|x| matches!(x.1, UTXOSpendInfo::HashlockContract { .. }))
.cloned()
.collect();
filtered_utxos
) -> impl Iterator<Item = (&ListUnspentResultEntry, &UTXOSpendInfo)> + '_ {
self.store
.utxo_cache
.values()
.filter(|(_, spend)| matches!(spend, &UTXOSpendInfo::HashlockContract { .. }))
.map(|(u, s)| (u, s))
}

/// Lists fidelity UTXOs along with their Spend info.
pub fn list_fidelity_spend_info(&self) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {
let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.filter(|x| matches!(x.1, UTXOSpendInfo::FidelityBondCoin { .. }))
.cloned()
.collect();
filtered_utxos
pub fn list_fidelity_spend_info(
&self,
) -> impl Iterator<Item = (&ListUnspentResultEntry, &UTXOSpendInfo)> + '_ {
self.store
.utxo_cache
.values()
.filter(|(_, spend)| matches!(spend, &UTXOSpendInfo::FidelityBondCoin { .. }))
.map(|(u, s)| (u, s))
}

/// Lists descriptor UTXOs along with their Spend info.
pub fn list_descriptor_utxo_spend_info(&self) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {
Comment on lines 801 to 802

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, why not change?

let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.into_iter()
.filter(|x| matches!(x.1, UTXOSpendInfo::SeedCoin { .. }))
.cloned()
.collect();
filtered_utxos
}
Expand All @@ -821,37 +812,33 @@ impl Wallet {
pub fn list_swap_coin_utxo_spend_info(&self) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't we changing this too?

let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.into_iter()
.filter(|x| {
matches!(
x.1,
UTXOSpendInfo::IncomingSwapCoin { .. } | UTXOSpendInfo::OutgoingSwapCoin { .. }
)
})
.cloned()
.collect();
filtered_utxos
}

/// Lists all incoming swapcoin UTXOs along with their Spend info.
pub fn list_incoming_swap_coin_utxo_spend_info(
&self,
) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {
let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.filter(|x| matches!(x.1, UTXOSpendInfo::IncomingSwapCoin { .. }))
.cloned()
.collect();
filtered_utxos
) -> impl Iterator<Item = (&ListUnspentResultEntry, &UTXOSpendInfo)> {
self.store
.utxo_cache
.values()
.filter(|(_, spend_info)| matches!(spend_info, UTXOSpendInfo::IncomingSwapCoin { .. }))
.map(|(utxo, spend_info)| (utxo, spend_info))
}
/// Lists all swept incoming swapcoin UTXOs along with their Spend info.
pub fn list_swept_incoming_swap_utxos(&self) -> Vec<(ListUnspentResultEntry, UTXOSpendInfo)> {
Comment on lines 836 to 837

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be changed.

let all_valid_utxo = self.list_all_utxo_spend_info();
let filtered_utxos: Vec<_> = all_valid_utxo
.iter()
.into_iter()
.filter(|(_, spend_info)| matches!(spend_info, UTXOSpendInfo::SweptCoin { .. }))
.cloned()
.collect();
filtered_utxos
}
Expand Down Expand Up @@ -1815,7 +1802,7 @@ impl Wallet {
internal_address
);
let sweep_tx = self.spend_coins(
&[(utxo.clone(), spend_info)],
&[(utxo.clone(), spend_info.clone())],
Destination::Sweep(internal_address.clone()),
feerate,
)?;
Expand Down
Loading
Loading