nautechsystems/nautilus_trader · error · anyhow::Error
Finalized transaction {} emitted {} Swap logs from expected
Error message
Finalized transaction {} emitted {} Swap logs from expected pool {}; expected exactly one What it means
After confirming a swap transaction is finalized, the library locates the single Swap log emitted by the expected pool address in the transaction receipt. If zero or multiple matching logs are found, the receipt cannot be mapped unambiguously to one swap event, so verification fails.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:4734
}
fn validate_finalized_swap_fill(
plan: &SwapPlan,
included: &IncludedTransaction,
) -> anyhow::Result<Option<FinalizedSwapFill>> {
let signature =
keccak256("Swap(address,address,int256,int256,uint160,uint128,int24)").to_string();
let swap_logs = included
.receipt
.logs
.iter()
.filter(|log| {
!log.removed
&& log.topics.first().is_some_and(|topic| topic == &signature)
&& Address::from_str(&log.address).ok() == Some(plan.pool_address)
})
.collect::<Vec<_>>();
anyhow::ensure!(
swap_logs.len() == 1,
"Finalized transaction {} emitted {} Swap logs from expected pool {}; expected exactly one",
included.tx_hash,
swap_logs.len(),
plan.pool_address
);
let log = swap_logs[0];
let log_transaction_hash = B256::from_str(&rpc_log::extract_transaction_hash(log)?)
.with_context(|| "Invalid finalized Swap log transaction hash")?;
let log_block_hash = log
.block_hash
.as_deref()
.ok_or_else(|| anyhow::anyhow!("Finalized Swap log has no block hash"))?;
anyhow::ensure!(
log_transaction_hash == included.tx_hash
&& rpc_log::extract_block_number(log)? == included.block_number
&& u64::from(rpc_log::extract_transaction_index(log)?)
== included.receipt.transaction_indexView on GitHub (pinned to 18893faf8b)
Solutions
- Confirm plan.pool_address matches the pool the router actually used for this tx (inspect the receipt logs)
- If the transaction legitimately contains multiple swaps, process each log individually instead of requiring exactly one
- Re-check the plan construction: ensure the pool chosen at quote time is the one the execution route targets
- Exclude aggregator intermediate logs by matching expected topic0 plus pool address, as done here, and verify the tx hash is the intended one
Example fix
// before
anyhow::ensure!(swap_logs.len() == 1, "expected exactly one");
// after
let log = swap_logs.first().ok_or_else(|| anyhow!("no Swap log"))?;
// or iterate swap_logs if multi-swap txs are expected Defensive patterns
Strategy: validation
Validate before calling
let swap_logs: Vec<_> = receipt.logs.iter().filter(|l| !l.removed && l.topics.first() == Some(&signature) && l.address == plan.pool_address).collect();
if swap_logs.len() != 1 { return Err(anyhow!("expected 1 Swap log, got {}", swap_logs.len())); } Try / catch
match verify_finalized_swap(plan, included).await {
Ok(swap) => handle_swap(swap),
Err(e) if e.to_string().contains("expected exactly one") => recheck_pool_route(e),
Err(e) => return Err(e),
} Prevention
- Record the exact pool address used by the execution route in the plan
- Reject multi-swap transactions at submission time or handle logs per-swap
- Log all receipt topics/addresses on mismatch to diagnose routing
- Keep pool addresses checksummed and chain-scoped in configuration
When it happens
Trigger: A finalized transaction whose receipt contains no Swap log from plan.pool_address (wrong pool configured in the plan, aggregator router swapped via a different pool) or more than one Swap log from the same pool (multihop/multi-swap transaction, flash-swap callback plus swap).
Common situations: Plan built against one pool but router executed the trade on another; transaction bundles several swaps through the same pool; MEV bot transaction with multiple swaps; pool address recorded with wrong case/chain that Address::from_str resolves differently.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- Finalized Swap input {base_amount} does not match the persis
- Finalized Swap has no calculated trade information
- No deployed bytecode at {description} address {address}
- HyperSync parsing of swap event is not defined in this dex:
- Unsupported order type {}; only Market is supported
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/8b6592933d35360b.
Report an issue: GitHub.