nautechsystems/nautilus_trader · error
Finalized Swap has no calculated trade information
Error message
Finalized Swap has no calculated trade information
What it means
After a finalized on-chain swap is built, the adapter calls `swap.calculate_trade_info` to derive trade details (price, side, quantity) from the pool tokens. This error is thrown when `calculate_trade_info` succeeded but the swap's `trade_info` field is still `None`, meaning the finalized Swap object carries no calculated trade information. It is an internal invariant check: a finalized swap should always yield trade info once calculated.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:4823
"Verified inclusion header {} does not match receipt hash {}",
included.block_number,
included.receipt.block_hash
);
let timestamp_ns = block
.timestamp
.checked_mul(NANOSECONDS_IN_SECOND)
.ok_or_else(|| anyhow::anyhow!("Finalized block timestamp overflows nanoseconds"))?;
let mut swap = event.to_pool_swap(
plan.pool.chain.clone(),
plan.instrument_id,
plan.pool.pool_identifier,
UnixNanos::from(timestamp_ns),
);
swap.calculate_trade_info(&plan.pool.token0, &plan.pool.token1, None)?;
let trade = swap
.trade_info
.as_ref()
.ok_or_else(|| anyhow::anyhow!("Finalized Swap has no calculated trade information"))?;
anyhow::ensure!(
trade.order_side == plan.order.order_side(),
"Finalized Swap side {} does not match {} order",
trade.order_side,
plan.order.order_side()
);
let gas_cost = included
.receipt
.effective_gas_price
.checked_mul(U256::from(included.receipt.gas_used))
.ok_or_else(|| anyhow::anyhow!("Finalized transaction gas commission overflow"))?;
let commission = Money::from_u256(gas_cost, plan.pool.chain.native_currency())?;
let trade_digest = keccak256(format!("{}:{}", included.tx_hash, swap.log_index));
let trade_digest = trade_digest.to_string();
let trade_id = TradeId::new_checked(&trade_digest[2..38])?;
if plan
.orderView on GitHub (pinned to 18893faf8b)
Solutions
- Verify the pool token pair (token0/token1) and decimals are correctly registered in the deployment manifest for this pool
- Check that the swap direction (token0->token1 or reverse) is one supported by `calculate_trade_info`
- Inspect the `Swap` construction code to confirm amounts/limits are set before finalization
- Upgrade or patch the blockchain adapter if `calculate_trade_info` has a known gap leaving `trade_info` as None
Example fix
// before
swap.calculate_trade_info(&plan.pool.token0, &plan.pool.token1, None)?;
// after: validate the swap can produce trade info first
if swap.amount_in.is_zero() || swap.amount_out.is_zero() {
anyhow::bail!("Swap amounts are zero; trade info cannot be calculated");
}
swap.calculate_trade_info(&plan.pool.token0, &plan.pool.token1, None)?; Defensive patterns
Strategy: try-catch
Validate before calling
// before execution: ensure the swap can yield trade info
if swap.amount_in.is_zero() || swap.amount_out.is_zero() {
return Err(anyhow::anyhow!("swap amounts are zero; trade info will not be calculated"));
} Type guard
fn has_trade_info(swap: &Swap) -> bool {
swap.trade_info.is_some()
} Try / catch
match swap.calculate_trade_info(&plan.pool.token0, &plan.pool.token1, None) {
Ok(_) if swap.trade_info.is_some() => { /* proceed */ }
Ok(_) => log::error!("trade info not populated; check pool token config"),
Err(e) => log::error!("trade info calculation failed: {e}"),
} Prevention
- Validate pool token addresses and decimals against the deployment manifest before building swaps
- Unit-test calculate_trade_info for both swap directions on every supported pool type
- Log the swap contents when trade_info is None to catch adapter gaps early
When it happens
Trigger: Executing a finalized DEX swap in `BlockchainExecutionClient` where `calculate_trade_info` leaves `swap.trade_info` unset (e.g. a swap variant that cannot compute trade info for the given token pair, missing decimals, or a malformed swap amount) despite `calculate_trade_info` returning `Ok`.
Common situations: Swapping through a pool whose token configuration or swap direction prevents trade-info computation; adapter bugs where a new swap type does not populate `trade_info`; token decimals or pool data fetched on-chain being inconsistent so calculation silently yields nothing.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Finalized transaction {} emitted {} Swap logs from expected
- Finalized Swap input {base_amount} does not match the persis
- Failed to load active execution intent: {e}
- Failed to start replacement transaction persistence: {e}
- Failed to lock active execution intent {intent_id}: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/633b97051efd283a.
Report an issue: GitHub.