// file: kb_lib/src/trade_amount_resolution.rs

//! Trade amount resolution orchestration.
//!
//! This module resolves base/quote raw amounts and quote/base price for one
//! decoded trade candidate by applying protocol-specific and generic fallback
//! strategies in deterministic order.

/// Input context required to resolve trade amounts.
pub(crate) struct TradeAmountResolutionInput<'a> {
    /// Database connection.
    pub(crate) database: &'a crate::Database,
    /// Persisted transaction row.
    pub(crate) transaction: &'a crate::ChainTransactionDto,
    /// Decoded DEX event row.
    pub(crate) decoded_event: &'a crate::DexDecodedEventDto,
    /// Decoded event payload.
    pub(crate) payload: &'a serde_json::Value,
    /// Pool account address.
    pub(crate) pool_address: &'a str,
    /// Base token mint, when known.
    pub(crate) base_token_mint: std::option::Option<&'a str>,
    /// Quote token mint, when known.
    pub(crate) quote_token_mint: std::option::Option<&'a str>,
    /// Base token decimals, when known.
    pub(crate) base_token_decimals: std::option::Option<u8>,
    /// Quote token decimals, when known.
    pub(crate) quote_token_decimals: std::option::Option<u8>,
    /// Base token vault address, when known.
    pub(crate) base_vault_address: std::option::Option<&'a str>,
    /// Quote token vault address, when known.
    pub(crate) quote_vault_address: std::option::Option<&'a str>,
}

/// Resolved raw trade amounts and quote/base price.
#[derive(Debug, Clone)]
pub(crate) struct TradeAmountResolution {
    /// Base amount in raw token units.
    pub(crate) base_amount_raw: std::option::Option<std::string::String>,
    /// Quote amount in raw token units.
    pub(crate) quote_amount_raw: std::option::Option<std::string::String>,
    /// Quote/base price.
    pub(crate) price_quote_per_base: std::option::Option<f64>,
    /// Trade side resolved from balance deltas, when available.
    pub(crate) resolved_trade_side: std::option::Option<crate::SwapTradeSide>,
}

/// Resolves trade amounts from payload and protocol-specific fallbacks.
pub(crate) async fn resolve_trade_amounts(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
) -> Result<crate::trade_amount_resolution::TradeAmountResolution, crate::Error> {
    let mut base_amount_raw = crate::trade_amount_resolution::extract_amount_string(
        input.payload,
        &["baseAmountRaw", "base_amount_raw", "baseAmount", "amountBase", "amountInBase"],
    );
    let mut quote_amount_raw = crate::trade_amount_resolution::extract_amount_string(
        input.payload,
        &[
            "quoteAmountRaw",
            "quote_amount_raw",
            "quoteAmount",
            "amountQuote",
            "amountOutQuote",
        ],
    );
    let mut price_quote_per_base = None;
    let mut resolved_trade_side = None;
    if input.decoded_event.event_kind.starts_with("pump_swap.")
        && (base_amount_raw.is_none()
            || quote_amount_raw.is_none()
            || price_quote_per_base.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_pump_swap_amount_fallbacks(
            input,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        )
        .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("pump_swap.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_pump_swap_db_instruction_transfer_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut resolved_trade_side,
            )
            .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("pump_fun.")
        && (base_amount_raw.is_none()
            || quote_amount_raw.is_none()
            || price_quote_per_base.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_pump_fun_amount_fallback(
            input,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        )
        .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if (input.decoded_event.event_kind.starts_with("raydium_amm_v4.")
        || input.decoded_event.event_kind.starts_with("raydium_cpmm.")
        || input.decoded_event.event_kind.starts_with("raydium_stable_swap.")
        || input.decoded_event.event_kind.starts_with("raydium_clmm."))
        && (base_amount_raw.is_none()
            || quote_amount_raw.is_none()
            || price_quote_per_base.is_none())
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_raydium_instruction_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut price_quote_per_base,
            )
            .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    let has_payload_trade_side = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["tradeSide", "trade_side"],
    )
    .is_some();
    if input.decoded_event.event_kind == "raydium_launchpad.trade_event"
        && (base_amount_raw.is_none()
            || quote_amount_raw.is_none()
            || (!has_payload_trade_side && resolved_trade_side.is_none()))
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_raydium_launchpad_sibling_instruction_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut resolved_trade_side,
            )
            .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind == "pump_swap.buy_exact_quote_in" {
        crate::trade_amount_resolution::normalize_pump_swap_anchor_buy_exact_quote_in_amounts(
            input,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut resolved_trade_side,
        );
    }
    if input.decoded_event.event_kind.starts_with("raydium_launchpad.")
        && (base_amount_raw.is_none()
            || quote_amount_raw.is_none()
            || price_quote_per_base.is_none())
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_raydium_launchpad_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut price_quote_per_base,
            );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("raydium_amm_v4.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
            input,
            input.base_vault_address,
            input.quote_vault_address,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("raydium_cpmm.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
            input,
            input.base_vault_address,
            input.quote_vault_address,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("raydium_stable_swap.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
            input,
            input.base_vault_address,
            input.quote_vault_address,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("raydium_clmm.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
            input,
            input.base_vault_address,
            input.quote_vault_address,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_dlmm.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_meteora_dlmm_flattened_cpi_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut resolved_trade_side,
            )
            .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_damm_v1.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_meteora_damm_v1_flattened_cpi_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut resolved_trade_side,
            )
            .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_dbc.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_meteora_dbc_payload_transfer_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut resolved_trade_side,
            )
            .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_dbc.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result =
            crate::trade_amount_resolution::apply_meteora_dbc_flattened_cpi_amount_fallback(
                input,
                &mut base_amount_raw,
                &mut quote_amount_raw,
                &mut resolved_trade_side,
            )
            .await;
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_dlmm.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
            input,
            input.base_vault_address,
            input.quote_vault_address,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_damm_v1.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
            input,
            input.base_vault_address,
            input.quote_vault_address,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_dbc.")
        && (base_amount_raw.is_none() || quote_amount_raw.is_none())
    {
        let resolution_result = crate::trade_amount_resolution::apply_vault_balance_delta_fallback(
            input,
            input.base_vault_address,
            input.quote_vault_address,
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut price_quote_per_base,
        );
        if let Err(error) = resolution_result {
            return Err(error);
        }
    }
    if input.decoded_event.event_kind.starts_with("raydium_amm_v4.") {
        let vault_side = crate::trade_amount_resolution::infer_trade_side_from_vault_balance_deltas(
            input.transaction.meta_json.as_deref(),
            input.transaction.transaction_json.as_str(),
            input.base_vault_address,
            input.quote_vault_address,
        );
        if vault_side.is_some() {
            resolved_trade_side = vault_side;
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_dlmm.") {
        let vault_side = crate::trade_amount_resolution::infer_trade_side_from_vault_balance_deltas(
            input.transaction.meta_json.as_deref(),
            input.transaction.transaction_json.as_str(),
            input.base_vault_address,
            input.quote_vault_address,
        );
        if vault_side.is_some() {
            resolved_trade_side = vault_side;
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_damm_v1.") {
        let vault_side = crate::trade_amount_resolution::infer_trade_side_from_vault_balance_deltas(
            input.transaction.meta_json.as_deref(),
            input.transaction.transaction_json.as_str(),
            input.base_vault_address,
            input.quote_vault_address,
        );
        if vault_side.is_some() {
            resolved_trade_side = vault_side;
        }
    }
    if input.decoded_event.event_kind.starts_with("meteora_dbc.") {
        let vault_side = crate::trade_amount_resolution::infer_trade_side_from_vault_balance_deltas(
            input.transaction.meta_json.as_deref(),
            input.transaction.transaction_json.as_str(),
            input.base_vault_address,
            input.quote_vault_address,
        );
        if vault_side.is_some() {
            resolved_trade_side = vault_side;
        }
    }
    if price_quote_per_base.is_none() {
        price_quote_per_base =
            crate::trade_metric_update::compute_price_quote_per_base_from_raw_amounts_with_decimals(
                base_amount_raw.as_deref(),
                quote_amount_raw.as_deref(),
                input.base_token_decimals,
                input.quote_token_decimals,
            );
    }
    if price_quote_per_base.is_none() {
        price_quote_per_base =
            crate::trade_solana_amounts::compute_price_quote_per_base_with_decimals(
                input.transaction.meta_json.as_deref(),
                input.transaction.transaction_json.as_str(),
                input.base_vault_address,
                input.quote_vault_address,
            );
    }
    if price_quote_per_base.is_none() {
        price_quote_per_base =
            crate::trade_metric_update::compute_price_quote_per_base_from_raw_amounts(
                base_amount_raw.as_deref(),
                quote_amount_raw.as_deref(),
            );
    }
    return Ok(crate::trade_amount_resolution::TradeAmountResolution {
        base_amount_raw,
        quote_amount_raw,
        price_quote_per_base,
        resolved_trade_side,
    });
}

async fn apply_pump_swap_amount_fallbacks(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    price_quote_per_base: &mut std::option::Option<f64>,
) -> Result<(), crate::Error> {
    let pool_owner_result = match (input.base_token_mint, input.quote_token_mint) {
        (Some(base_mint), Some(quote_mint)) => {
            crate::trade_pump_swap_amounts::resolve_pump_swap_trade_amounts_from_pool_balance_deltas(
                input.transaction.meta_json.as_deref(),
                input.pool_address,
                base_mint,
                quote_mint,
                input.decoded_event.event_kind.as_str(),
                input.base_token_decimals,
                input.quote_token_decimals,
            )
        },
        _ => Ok(crate::trade_pump_swap_amounts::PumpSwapPoolBalanceDeltaResolution::MissingData),
    };
    let pool_owner_resolution = match pool_owner_result {
        Ok(pool_owner_resolution) => pool_owner_resolution,
        Err(error) => return Err(error),
    };
    let pool_owner_resolution_label = pool_owner_resolution.as_label();
    tracing::debug!(
        event_kind = %input.decoded_event.event_kind,
        pool_account = ?input.decoded_event.pool_account,
        decoded_event_id = ?input.decoded_event.id,
        transaction_signature = %input.transaction.signature,
        base_mint = ?input.base_token_mint,
        quote_mint = ?input.quote_token_mint,
        pool_owner_resolution = %pool_owner_resolution_label,
        "pump_swap pool-owner delta resolution result"
    );
    match pool_owner_resolution {
        crate::trade_pump_swap_amounts::PumpSwapPoolBalanceDeltaResolution::Matched(amounts) => {
            *base_amount_raw = Some(amounts.base_amount_raw);
            *quote_amount_raw = Some(amounts.quote_amount_raw);
            *price_quote_per_base = Some(amounts.price_quote_per_base);
            tracing::debug!(
                event_kind = %input.decoded_event.event_kind,
                pool_account = ?input.decoded_event.pool_account,
                decoded_event_id = ?input.decoded_event.id,
                base_mint = ?input.base_token_mint,
                quote_mint = ?input.quote_token_mint,
                base_amount_raw = ?base_amount_raw,
                quote_amount_raw = ?quote_amount_raw,
                price_quote_per_base = ?price_quote_per_base,
                "pump_swap trade amounts recovered from pool-owner token balance deltas"
            );
        },
        crate::trade_pump_swap_amounts::PumpSwapPoolBalanceDeltaResolution::DirectionMismatch => {
            tracing::debug!(
                event_kind = %input.decoded_event.event_kind,
                pool_account = ?input.decoded_event.pool_account,
                decoded_event_id = ?input.decoded_event.id,
                transaction_signature = %input.transaction.signature,
                "pump_swap pool-owner full-transaction delta direction mismatch; continuing with instruction-scoped fallbacks"
            );
        },
        crate::trade_pump_swap_amounts::PumpSwapPoolBalanceDeltaResolution::MissingData => {},
    }
    let decoded_instruction_index_result =
        crate::trade_amount_resolution::load_decoded_instruction_index(
            input.database,
            input.decoded_event,
        )
        .await;
    let decoded_instruction_index = match decoded_instruction_index_result {
        Ok(decoded_instruction_index) => decoded_instruction_index,
        Err(error) => return Err(error),
    };
    let payload_user_base_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["userBaseTokenAccount", "user_base_token_account"],
        );
    let payload_user_quote_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["userQuoteTokenAccount", "user_quote_token_account"],
        );
    let payload_pool_base_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["poolBaseTokenAccount", "pool_base_token_account"],
        );
    let payload_pool_quote_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["poolQuoteTokenAccount", "pool_quote_token_account"],
        );
    let effective_base_vault_address = match input.base_vault_address {
        Some(base_vault_address) => Some(base_vault_address),
        None => payload_pool_base_token_account.as_deref(),
    };
    let effective_quote_vault_address = match input.quote_vault_address {
        Some(quote_vault_address) => Some(quote_vault_address),
        None => payload_pool_quote_token_account.as_deref(),
    };
    let (input_vault_address, output_vault_address, input_token_account, output_token_account) =
        if input.decoded_event.event_kind.ends_with(".buy")
            || input.decoded_event.event_kind.ends_with(".buy_exact_quote_in")
        {
            (
                effective_quote_vault_address,
                effective_base_vault_address,
                payload_user_quote_token_account.as_deref(),
                payload_user_base_token_account.as_deref(),
            )
        } else if input.decoded_event.event_kind.ends_with(".sell") {
            (
                effective_base_vault_address,
                effective_quote_vault_address,
                payload_user_base_token_account.as_deref(),
                payload_user_quote_token_account.as_deref(),
            )
        } else {
            (None, None, None, None)
        };
    let inferred_result =
        crate::trade_solana_amounts::extract_trade_amounts_from_instruction_token_transfers(
            input.transaction.meta_json.as_deref(),
            decoded_instruction_index,
            input_vault_address,
            output_vault_address,
            input_token_account,
            output_token_account,
            effective_base_vault_address,
            effective_quote_vault_address,
        );
    let inferred = match inferred_result {
        Ok(inferred) => inferred,
        Err(error) => return Err(error),
    };
    if base_amount_raw.is_none() {
        *base_amount_raw = inferred.0;
    }
    if quote_amount_raw.is_none() {
        *quote_amount_raw = inferred.1;
    }
    if price_quote_per_base.is_none() {
        *price_quote_per_base = inferred.2;
    }
    if base_amount_raw.is_none() || quote_amount_raw.is_none() {
        let fallback_result =
            crate::trade_solana_amounts::extract_trade_amounts_from_vault_balance_deltas(
                input.transaction.transaction_json.as_str(),
                input.transaction.meta_json.as_deref(),
                effective_base_vault_address,
                effective_quote_vault_address,
            );
        let fallback = match fallback_result {
            Ok(fallback) => fallback,
            Err(error) => return Err(error),
        };
        if base_amount_raw.is_none() {
            *base_amount_raw = fallback.0;
        }
        if quote_amount_raw.is_none() {
            *quote_amount_raw = fallback.1;
        }
        if price_quote_per_base.is_none() {
            *price_quote_per_base = fallback.2;
        }
    }

    if base_amount_raw.is_none() || quote_amount_raw.is_none() || price_quote_per_base.is_none() {
        let transaction_value_result =
            crate::trade_pump_swap_amounts::build_transaction_value_with_meta_json(
                input.transaction.transaction_json.as_str(),
                input.transaction.meta_json.as_deref(),
            );
        let transaction_value = match transaction_value_result {
            Ok(transaction_value) => transaction_value,
            Err(error) => return Err(error),
        };
        let fallback_amounts = match (input.base_token_mint, input.quote_token_mint) {
            (Some(base_mint), Some(quote_mint)) => {
                crate::trade_pump_swap_amounts::try_build_pump_swap_trade_amounts_from_token_balance_deltas(
                    &transaction_value,
                    base_mint,
                    quote_mint,
                )
            },
            _ => None,
        };
        if let Some(fallback_amounts) = fallback_amounts {
            if base_amount_raw.is_none() {
                *base_amount_raw = crate::trade_pump_swap_amounts::convert_ui_amount_to_raw_string(
                    fallback_amounts.base_amount,
                    input.base_token_decimals,
                );
            }
            if quote_amount_raw.is_none() {
                *quote_amount_raw = crate::trade_pump_swap_amounts::convert_ui_amount_to_raw_string(
                    fallback_amounts.quote_amount,
                    input.quote_token_decimals,
                );
            }
            if price_quote_per_base.is_none() {
                *price_quote_per_base = Some(fallback_amounts.price_quote_per_base);
            }
            tracing::debug!(
                event_kind = %input.decoded_event.event_kind,
                pool_account = ?input.decoded_event.pool_account,
                decoded_event_id = ?input.decoded_event.id,
                base_mint = ?input.base_token_mint,
                quote_mint = ?input.quote_token_mint,
                base_amount_raw = ?base_amount_raw,
                quote_amount_raw = ?quote_amount_raw,
                price_quote_per_base = ?price_quote_per_base,
                "pump_swap trade amounts recovered from token balance deltas"
            );
        }
    }
    if price_quote_per_base.is_none() && base_amount_raw.is_some() && quote_amount_raw.is_some() {
        *price_quote_per_base =
            crate::trade_metric_update::compute_price_quote_per_base_from_raw_amounts_with_decimals(
                base_amount_raw.as_ref().map(std::string::String::as_str),
                quote_amount_raw.as_ref().map(std::string::String::as_str),
                input.base_token_decimals,
                input.quote_token_decimals,
            );
        tracing::debug!(
            event_kind = %input.decoded_event.event_kind,
            pool_account = ?input.decoded_event.pool_account,
            decoded_event_id = ?input.decoded_event.id,
            base_amount_raw = ?base_amount_raw,
            quote_amount_raw = ?quote_amount_raw,
            price_quote_per_base = ?price_quote_per_base,
            "pump_swap trade price computed from resolved raw amounts"
        );
    }
    return Ok(());
}


async fn apply_pump_swap_db_instruction_transfer_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) -> Result<(), crate::Error> {
    if base_amount_raw.is_some() && quote_amount_raw.is_some() {
        return Ok(());
    }
    let decoded_instruction_result = crate::trade_amount_resolution::load_decoded_instruction(
        input.database,
        input.decoded_event,
    )
    .await;
    let decoded_instruction = match decoded_instruction_result {
        Ok(Some(decoded_instruction)) => decoded_instruction,
        Ok(None) => return Ok(()),
        Err(error) => return Err(error),
    };
    let instructions_result = crate::query_chain_instructions_list_by_transaction_id(
        input.database,
        input.decoded_event.transaction_id,
    )
    .await;
    let instructions = match instructions_result {
        Ok(instructions) => instructions,
        Err(error) => return Err(error),
    };
    let payload_user_base_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["userBaseTokenAccount", "user_base_token_account"],
        );
    let payload_user_quote_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["userQuoteTokenAccount", "user_quote_token_account"],
        );
    let payload_pool_base_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["poolBaseTokenAccount", "pool_base_token_account", "baseVault", "base_vault"],
        );
    let payload_pool_quote_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["poolQuoteTokenAccount", "pool_quote_token_account", "quoteVault", "quote_vault"],
        );
    let pool_base_token_account = match input.base_vault_address {
        Some(base_vault_address) => Some(base_vault_address),
        None => payload_pool_base_token_account.as_deref(),
    };
    let pool_quote_token_account = match input.quote_vault_address {
        Some(quote_vault_address) => Some(quote_vault_address),
        None => payload_pool_quote_token_account.as_deref(),
    };
    let user_base_token_account = payload_user_base_token_account.as_deref();
    let user_quote_token_account = payload_user_quote_token_account.as_deref();
    let is_buy = input.decoded_event.event_kind.ends_with(".buy")
        || input.decoded_event.event_kind.ends_with(".buy_exact_quote_in");
    let is_sell = input.decoded_event.event_kind.ends_with(".sell");
    if !is_buy && !is_sell {
        return Ok(());
    }
    let mut base_transfer_direction = None;
    let mut quote_transfer_direction = None;
    for instruction in &instructions {
        if !crate::trade_amount_resolution::instruction_is_inside_same_pump_swap_instruction_window(
            &decoded_instruction,
            instruction,
        ) {
            continue;
        }
        let parsed_transfer_result =
            crate::trade_amount_resolution::parse_transfer_checked_instruction(instruction);
        let parsed_transfer = match parsed_transfer_result {
            Ok(Some(parsed_transfer)) => parsed_transfer,
            Ok(None) => continue,
            Err(error) => return Err(error),
        };
        if is_buy {
            if base_amount_raw.is_none()
                && crate::trade_amount_resolution::string_option_equals(
                    input.base_token_mint,
                    parsed_transfer.mint.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    pool_base_token_account,
                    parsed_transfer.source.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    user_base_token_account,
                    parsed_transfer.destination.as_str(),
                )
            {
                *base_amount_raw = Some(parsed_transfer.amount_raw.clone());
                base_transfer_direction =
                    Some(crate::trade_amount_resolution::VaultTransferDirection::OutOfVault);
                continue;
            }
            if quote_amount_raw.is_none()
                && crate::trade_amount_resolution::string_option_equals(
                    input.quote_token_mint,
                    parsed_transfer.mint.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    user_quote_token_account,
                    parsed_transfer.source.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    pool_quote_token_account,
                    parsed_transfer.destination.as_str(),
                )
            {
                *quote_amount_raw = Some(parsed_transfer.amount_raw.clone());
                quote_transfer_direction =
                    Some(crate::trade_amount_resolution::VaultTransferDirection::IntoVault);
                continue;
            }
        }
        if is_sell {
            if base_amount_raw.is_none()
                && crate::trade_amount_resolution::string_option_equals(
                    input.base_token_mint,
                    parsed_transfer.mint.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    user_base_token_account,
                    parsed_transfer.source.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    pool_base_token_account,
                    parsed_transfer.destination.as_str(),
                )
            {
                *base_amount_raw = Some(parsed_transfer.amount_raw.clone());
                base_transfer_direction =
                    Some(crate::trade_amount_resolution::VaultTransferDirection::IntoVault);
                continue;
            }
            if quote_amount_raw.is_none()
                && crate::trade_amount_resolution::string_option_equals(
                    input.quote_token_mint,
                    parsed_transfer.mint.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    pool_quote_token_account,
                    parsed_transfer.source.as_str(),
                )
                && crate::trade_amount_resolution::account_option_equals(
                    user_quote_token_account,
                    parsed_transfer.destination.as_str(),
                )
            {
                *quote_amount_raw = Some(parsed_transfer.amount_raw.clone());
                quote_transfer_direction =
                    Some(crate::trade_amount_resolution::VaultTransferDirection::OutOfVault);
                continue;
            }
        }
    }
    if resolved_trade_side.is_none() {
        *resolved_trade_side =
            crate::trade_amount_resolution::infer_trade_side_from_transfer_directions(
                base_transfer_direction,
                quote_transfer_direction,
            );
    }
    if base_amount_raw.is_some() || quote_amount_raw.is_some() {
        tracing::debug!(
            event_kind = %input.decoded_event.event_kind,
            decoded_event_id = ?input.decoded_event.id,
            transaction_signature = %input.transaction.signature,
            base_amount_raw = ?base_amount_raw,
            quote_amount_raw = ?quote_amount_raw,
            resolved_trade_side = ?resolved_trade_side,
            "pump_swap trade amounts recovered from persisted instruction transfer window"
        );
    }
    return Ok(());
}

fn instruction_is_inside_same_pump_swap_instruction_window(
    decoded_instruction: &crate::ChainInstructionDto,
    candidate_instruction: &crate::ChainInstructionDto,
) -> bool {
    if candidate_instruction.transaction_id != decoded_instruction.transaction_id {
        return false;
    }
    if candidate_instruction.instruction_index != decoded_instruction.instruction_index {
        return false;
    }
    let candidate_inner_instruction_index = match candidate_instruction.inner_instruction_index {
        Some(candidate_inner_instruction_index) => candidate_inner_instruction_index,
        None => return false,
    };
    if let Some(decoded_inner_instruction_index) = decoded_instruction.inner_instruction_index {
        if candidate_inner_instruction_index <= decoded_inner_instruction_index {
            return false;
        }
    }
    return true;
}

fn account_option_equals(left: std::option::Option<&str>, right: &str) -> bool {
    let left = match left {
        Some(left) => left,
        None => return false,
    };
    return crate::trade_amount_resolution::account_equals(left, right);
}

fn apply_raydium_launchpad_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    price_quote_per_base: &mut std::option::Option<f64>,
) -> Result<(), crate::Error> {
    if base_amount_raw.is_some() && quote_amount_raw.is_some() && price_quote_per_base.is_some() {
        return Ok(());
    }
    let transaction_value_result =
        crate::trade_pump_swap_amounts::build_transaction_value_with_meta_json(
            input.transaction.transaction_json.as_str(),
            input.transaction.meta_json.as_deref(),
        );
    let transaction_value = match transaction_value_result {
        Ok(transaction_value) => transaction_value,
        Err(error) => return Err(error),
    };
    let fallback_amounts = match (input.base_token_mint, input.quote_token_mint) {
        (Some(base_mint), Some(quote_mint)) => {
            crate::trade_pump_swap_amounts::try_build_pump_swap_trade_amounts_from_token_balance_deltas(
                &transaction_value,
                base_mint,
                quote_mint,
            )
        },
        _ => None,
    };
    let fallback_amounts = match fallback_amounts {
        Some(fallback_amounts) => fallback_amounts,
        None => return Ok(()),
    };
    if base_amount_raw.is_none() {
        *base_amount_raw = crate::trade_pump_swap_amounts::convert_ui_amount_to_raw_string(
            fallback_amounts.base_amount,
            input.base_token_decimals,
        );
    }
    if quote_amount_raw.is_none() {
        *quote_amount_raw = crate::trade_pump_swap_amounts::convert_ui_amount_to_raw_string(
            fallback_amounts.quote_amount,
            input.quote_token_decimals,
        );
    }
    if price_quote_per_base.is_none() {
        *price_quote_per_base = Some(fallback_amounts.price_quote_per_base);
    }
    tracing::debug!(
        event_kind = %input.decoded_event.event_kind,
        pool_account = ?input.decoded_event.pool_account,
        decoded_event_id = ?input.decoded_event.id,
        base_mint = ?input.base_token_mint,
        quote_mint = ?input.quote_token_mint,
        base_amount_raw = ?base_amount_raw,
        quote_amount_raw = ?quote_amount_raw,
        price_quote_per_base = ?price_quote_per_base,
        "raydium_launchpad trade amounts recovered from transaction token balance deltas"
    );
    return Ok(());
}

async fn apply_raydium_launchpad_sibling_instruction_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) -> Result<(), crate::Error> {
    if base_amount_raw.is_some() && quote_amount_raw.is_some() && resolved_trade_side.is_some() {
        return Ok(());
    }
    let amount_in_raw = crate::trade_amount_resolution::extract_amount_string(
        input.payload,
        &["amountInRaw", "amount_in_raw", "amountIn", "amount_in"],
    );
    let amount_out_raw = crate::trade_amount_resolution::extract_amount_string(
        input.payload,
        &["amountOutRaw", "amount_out_raw", "amountOut", "amount_out"],
    );
    let amount_in_raw = match amount_in_raw {
        Some(amount_in_raw) => amount_in_raw,
        None => return Ok(()),
    };
    let amount_out_raw = match amount_out_raw {
        Some(amount_out_raw) => amount_out_raw,
        None => return Ok(()),
    };
    let sibling_side_result =
        crate::trade_amount_resolution::resolve_raydium_launchpad_sibling_instruction_side(input)
            .await;
    let sibling_side = match sibling_side_result {
        Ok(sibling_side) => sibling_side,
        Err(error) => return Err(error),
    };
    let sibling_side = match sibling_side {
        Some(sibling_side) => sibling_side,
        None => return Ok(()),
    };
    crate::trade_amount_resolution::apply_raydium_launchpad_side_amount_mapping(
        sibling_side,
        amount_in_raw.as_str(),
        amount_out_raw.as_str(),
        base_amount_raw,
        quote_amount_raw,
        resolved_trade_side,
    );
    tracing::debug!(
        event_kind = %input.decoded_event.event_kind,
        pool_account = ?input.decoded_event.pool_account,
        decoded_event_id = ?input.decoded_event.id,
        transaction_signature = %input.transaction.signature,
        base_amount_raw = ?base_amount_raw,
        quote_amount_raw = ?quote_amount_raw,
        resolved_trade_side = ?resolved_trade_side,
        "raydium_launchpad trade amounts recovered from sibling swap instruction"
    );
    return Ok(());
}

async fn resolve_raydium_launchpad_sibling_instruction_side(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
) -> Result<std::option::Option<crate::SwapTradeSide>, crate::Error> {
    let decoded_events_result = crate::query_dex_decoded_events_list_by_transaction_id(
        input.database,
        input.decoded_event.transaction_id,
    )
    .await;
    let decoded_events = match decoded_events_result {
        Ok(decoded_events) => decoded_events,
        Err(error) => return Err(error),
    };
    let mut resolved_side = None;
    for decoded_event in decoded_events {
        if decoded_event.id == input.decoded_event.id {
            continue;
        }
        if decoded_event.protocol_name != "raydium_launchpad" {
            continue;
        }
        if decoded_event.pool_account.as_deref() != Some(input.pool_address) {
            continue;
        }
        let side = crate::trade_amount_resolution::raydium_launchpad_instruction_side(
            decoded_event.event_kind.as_str(),
        );
        let side = match side {
            Some(side) => side,
            None => continue,
        };
        match resolved_side {
            Some(existing_side) => {
                if existing_side != side {
                    tracing::debug!(
                        transaction_signature = %input.transaction.signature,
                        pool_account = %input.pool_address,
                        decoded_event_id = ?input.decoded_event.id,
                        existing_side = ?existing_side,
                        conflicting_side = ?side,
                        "raydium_launchpad sibling swap instruction side is ambiguous"
                    );
                    return Ok(None);
                }
            },
            None => resolved_side = Some(side),
        }
    }
    return Ok(resolved_side);
}

fn raydium_launchpad_instruction_side(
    event_kind: &str,
) -> std::option::Option<crate::SwapTradeSide> {
    match event_kind {
        "raydium_launchpad.buy_exact_in" | "raydium_launchpad.buy_exact_out" => {
            return Some(crate::SwapTradeSide::BuyBase);
        },
        "raydium_launchpad.sell_exact_in" | "raydium_launchpad.sell_exact_out" => {
            return Some(crate::SwapTradeSide::SellBase);
        },
        _ => return None,
    }
}

fn apply_raydium_launchpad_side_amount_mapping(
    side: crate::SwapTradeSide,
    amount_in_raw: &str,
    amount_out_raw: &str,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) {
    match side {
        crate::SwapTradeSide::BuyBase => {
            if base_amount_raw.is_none() {
                *base_amount_raw = Some(amount_out_raw.to_string());
            }
            if quote_amount_raw.is_none() {
                *quote_amount_raw = Some(amount_in_raw.to_string());
            }
            if resolved_trade_side.is_none() {
                *resolved_trade_side = Some(crate::SwapTradeSide::BuyBase);
            }
        },
        crate::SwapTradeSide::SellBase => {
            if base_amount_raw.is_none() {
                *base_amount_raw = Some(amount_in_raw.to_string());
            }
            if quote_amount_raw.is_none() {
                *quote_amount_raw = Some(amount_out_raw.to_string());
            }
            if resolved_trade_side.is_none() {
                *resolved_trade_side = Some(crate::SwapTradeSide::SellBase);
            }
        },
        crate::SwapTradeSide::Unknown => {},
    }
}

async fn apply_pump_fun_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    price_quote_per_base: &mut std::option::Option<f64>,
) -> Result<(), crate::Error> {
    let inferred_result = crate::trade_solana_amounts::extract_pump_fun_amounts_from_transaction(
        input.transaction.transaction_json.as_str(),
        input.transaction.meta_json.as_deref(),
        input.base_vault_address,
        input.quote_vault_address,
    );
    let inferred = match inferred_result {
        Ok(inferred) => inferred,
        Err(error) => return Err(error),
    };
    if base_amount_raw.is_none() {
        *base_amount_raw = inferred.0;
    }
    if quote_amount_raw.is_none() {
        *quote_amount_raw = inferred.1;
    }
    if price_quote_per_base.is_none() {
        *price_quote_per_base = inferred.2;
    }
    if base_amount_raw.is_none() || quote_amount_raw.is_none() || price_quote_per_base.is_none() {
        let sibling_result =
            crate::trade_amount_resolution::apply_pump_fun_trade_event_sibling_amount_fallback(
                input,
                base_amount_raw,
                quote_amount_raw,
                price_quote_per_base,
            )
            .await;
        if let Err(error) = sibling_result {
            return Err(error);
        }
    }
    return Ok(());
}

async fn apply_pump_fun_trade_event_sibling_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    price_quote_per_base: &mut std::option::Option<f64>,
) -> Result<(), crate::Error> {
    if !crate::trade_amount_resolution::pump_fun_instruction_trade_can_use_trade_event_fallback(
        input.decoded_event.event_kind.as_str(),
    ) {
        return Ok(());
    }
    let sibling_events_result = crate::query_dex_decoded_events_list_by_transaction_id(
        input.database,
        input.decoded_event.transaction_id,
    )
    .await;
    let sibling_events = match sibling_events_result {
        Ok(sibling_events) => sibling_events,
        Err(error) => return Err(error),
    };
    for sibling_event in sibling_events {
        if sibling_event.id == input.decoded_event.id {
            continue;
        }
        if sibling_event.protocol_name.as_str() != "pump_fun" {
            continue;
        }
        if sibling_event.event_kind.as_str() != "pump_fun.trade_event" {
            continue;
        }
        let sibling_payload_result =
            serde_json::from_str::<serde_json::Value>(sibling_event.payload_json.as_str());
        let sibling_payload = match sibling_payload_result {
            Ok(sibling_payload) => sibling_payload,
            Err(error) => {
                tracing::debug!(
                    decoded_event_id = ?sibling_event.id,
                    error = %error,
                    "cannot parse pump_fun trade_event sibling payload for amount fallback"
                );
                continue;
            },
        };
        if !crate::trade_amount_resolution::pump_fun_trade_event_sibling_matches_instruction(
            input.decoded_event.event_kind.as_str(),
            input.payload,
            &sibling_payload,
        ) {
            continue;
        }
        let sibling_base_amount = crate::trade_amount_resolution::extract_amount_string(
            &sibling_payload,
            &["baseAmountRaw", "baseAmount", "token_amount", "tokenAmount"],
        );
        let sibling_quote_amount = crate::trade_amount_resolution::extract_amount_string(
            &sibling_payload,
            &["quoteAmountRaw", "quoteAmount", "sol_amount", "solAmount", "quote_amount"],
        );
        if base_amount_raw.is_none() {
            *base_amount_raw = sibling_base_amount;
        }
        if quote_amount_raw.is_none() {
            *quote_amount_raw = sibling_quote_amount;
        }
        if price_quote_per_base.is_none() {
            *price_quote_per_base = crate::trade_metric_update::compute_price_quote_per_base_from_raw_amounts_with_decimals(
                base_amount_raw.as_deref(),
                quote_amount_raw.as_deref(),
                input.base_token_decimals,
                input.quote_token_decimals,
            );
        }
        tracing::debug!(
            event_kind = %input.decoded_event.event_kind,
            decoded_event_id = ?input.decoded_event.id,
            sibling_decoded_event_id = ?sibling_event.id,
            base_amount_raw = ?base_amount_raw,
            quote_amount_raw = ?quote_amount_raw,
            price_quote_per_base = ?price_quote_per_base,
            "pump_fun instruction amounts recovered from sibling trade_event"
        );
        if base_amount_raw.is_some() && quote_amount_raw.is_some() {
            return Ok(());
        }
    }
    return Ok(());
}

fn pump_fun_instruction_trade_can_use_trade_event_fallback(event_kind: &str) -> bool {
    match event_kind {
        "pump_fun.buy_exact_quote_in_v2" => return true,
        "pump_fun.buy_exact_sol_in" => return true,
        "pump_fun.buy_v2" => return true,
        "pump_fun.sell_v2" => return true,
        _ => return false,
    }
}

fn pump_fun_trade_event_sibling_matches_instruction(
    instruction_event_kind: &str,
    instruction_payload: &serde_json::Value,
    trade_event_payload: &serde_json::Value,
) -> bool {
    let expected_is_buy = match instruction_event_kind {
        "pump_fun.buy_exact_quote_in_v2" => Some(true),
        "pump_fun.buy_exact_sol_in" => Some(true),
        "pump_fun.buy_v2" => Some(true),
        "pump_fun.sell_v2" => Some(false),
        _ => None,
    };
    if let Some(expected_is_buy) = expected_is_buy {
        let actual_is_buy = crate::trade_amount_resolution::extract_bool_by_candidate_keys(
            trade_event_payload,
            &["is_buy", "isBuy"],
        );
        match actual_is_buy {
            Some(actual_is_buy) if actual_is_buy == expected_is_buy => {},
            Some(_) => return false,
            None => {},
        }
    }
    let instruction_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        instruction_payload,
        &["mint", "tokenMint", "tokenAMint"],
    );
    let trade_event_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        trade_event_payload,
        &["mint", "tokenMint", "tokenAMint"],
    );
    if !crate::trade_amount_resolution::optional_string_values_match(
        instruction_mint.as_deref(),
        trade_event_mint.as_deref(),
    ) {
        return false;
    }
    let instruction_user = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        instruction_payload,
        &["user", "actorWallet"],
    );
    let trade_event_user = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        trade_event_payload,
        &["user", "actorWallet"],
    );
    if !crate::trade_amount_resolution::optional_string_values_match(
        instruction_user.as_deref(),
        trade_event_user.as_deref(),
    ) {
        return false;
    }
    return true;
}

fn optional_string_values_match(
    left: std::option::Option<&str>,
    right: std::option::Option<&str>,
) -> bool {
    match (left, right) {
        (Some(left), Some(right)) => return left == right,
        _ => return true,
    }
}

async fn apply_raydium_instruction_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    price_quote_per_base: &mut std::option::Option<f64>,
) -> Result<(), crate::Error> {
    let decoded_instruction_index_result =
        crate::trade_amount_resolution::load_decoded_instruction_index(
            input.database,
            input.decoded_event,
        )
        .await;
    let decoded_instruction_index = match decoded_instruction_index_result {
        Ok(decoded_instruction_index) => decoded_instruction_index,
        Err(error) => return Err(error),
    };
    let payload_input_vault_address =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["inputVault", "input_vault"],
        );
    let payload_output_vault_address =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["outputVault", "output_vault"],
        );
    let payload_input_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["inputTokenAccount", "input_token_account"],
        );
    let payload_output_token_account =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["outputTokenAccount", "output_token_account"],
        );
    let payload_base_vault_address =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["baseVault", "base_vault"],
        );
    let payload_quote_vault_address =
        crate::trade_amount_resolution::extract_string_by_candidate_keys(
            input.payload,
            &["quoteVault", "quote_vault"],
        );
    let effective_base_vault_address = match input.base_vault_address {
        Some(base_vault_address) => Some(base_vault_address),
        None => payload_base_vault_address.as_deref(),
    };
    let effective_quote_vault_address = match input.quote_vault_address {
        Some(quote_vault_address) => Some(quote_vault_address),
        None => payload_quote_vault_address.as_deref(),
    };
    let inferred_result =
        crate::trade_solana_amounts::extract_trade_amounts_from_instruction_token_transfers(
            input.transaction.meta_json.as_deref(),
            decoded_instruction_index,
            payload_input_vault_address.as_deref(),
            payload_output_vault_address.as_deref(),
            payload_input_token_account.as_deref(),
            payload_output_token_account.as_deref(),
            effective_base_vault_address,
            effective_quote_vault_address,
        );
    let inferred = match inferred_result {
        Ok(inferred) => inferred,
        Err(error) => return Err(error),
    };
    if base_amount_raw.is_none() {
        *base_amount_raw = inferred.0;
    }
    if quote_amount_raw.is_none() {
        *quote_amount_raw = inferred.1;
    }
    if price_quote_per_base.is_none() {
        *price_quote_per_base = inferred.2;
    }
    return Ok(());
}

#[derive(Debug, Clone)]
struct FlattenedCpiTransferAmountResolution {
    base_amount_raw: std::option::Option<std::string::String>,
    quote_amount_raw: std::option::Option<std::string::String>,
    resolved_trade_side: std::option::Option<crate::SwapTradeSide>,
}

async fn apply_meteora_dlmm_flattened_cpi_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) -> Result<(), crate::Error> {
    return crate::trade_amount_resolution::apply_flattened_cpi_amount_fallback(
        input,
        "meteora_dlmm",
        base_amount_raw,
        quote_amount_raw,
        resolved_trade_side,
    )
    .await;
}

async fn apply_meteora_damm_v1_flattened_cpi_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) -> Result<(), crate::Error> {
    return crate::trade_amount_resolution::apply_flattened_cpi_amount_fallback(
        input,
        "meteora_damm_v1",
        base_amount_raw,
        quote_amount_raw,
        resolved_trade_side,
    )
    .await;
}

async fn apply_meteora_dbc_flattened_cpi_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) -> Result<(), crate::Error> {
    return crate::trade_amount_resolution::apply_flattened_cpi_amount_fallback(
        input,
        "meteora_dbc",
        base_amount_raw,
        quote_amount_raw,
        resolved_trade_side,
    )
    .await;
}

async fn apply_meteora_dbc_payload_transfer_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) -> Result<(), crate::Error> {
    let payload_token_a_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["tokenAMint", "token_a_mint", "baseMint", "base_mint"],
    );
    let payload_token_b_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["tokenBMint", "token_b_mint", "quoteMint", "quote_mint"],
    );
    let payload_base_vault = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["baseVault", "base_vault", "baseVaultAddress", "base_vault_address"],
    );
    let payload_quote_vault = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["quoteVault", "quote_vault", "quoteVaultAddress", "quote_vault_address"],
    );
    let payload_token_a_mint = match payload_token_a_mint {
        Some(payload_token_a_mint) => payload_token_a_mint,
        None => return Ok(()),
    };
    let payload_token_b_mint = match payload_token_b_mint {
        Some(payload_token_b_mint) => payload_token_b_mint,
        None => return Ok(()),
    };
    let payload_base_vault = match payload_base_vault {
        Some(payload_base_vault) => payload_base_vault,
        None => return Ok(()),
    };
    let payload_quote_vault = match payload_quote_vault {
        Some(payload_quote_vault) => payload_quote_vault,
        None => return Ok(()),
    };
    let decoded_instruction_result = crate::trade_amount_resolution::load_decoded_instruction(
        input.database,
        input.decoded_event,
    )
    .await;
    let decoded_instruction = match decoded_instruction_result {
        Ok(Some(decoded_instruction)) => decoded_instruction,
        Ok(None) => return Ok(()),
        Err(error) => return Err(error),
    };
    let instructions_result = crate::query_chain_instructions_list_by_transaction_id(
        input.database,
        input.decoded_event.transaction_id,
    )
    .await;
    let instructions = match instructions_result {
        Ok(instructions) => instructions,
        Err(error) => return Err(error),
    };
    let payload_order_result = resolve_amounts_from_flattened_cpi_transfer_window(
        &decoded_instruction,
        &instructions,
        Some(payload_token_a_mint.as_str()),
        Some(payload_token_b_mint.as_str()),
        Some(payload_base_vault.as_str()),
        Some(payload_quote_vault.as_str()),
    );
    let payload_order = match payload_order_result {
        Ok(payload_order) => payload_order,
        Err(error) => return Err(error),
    };
    if payload_order.base_amount_raw.is_none() || payload_order.quote_amount_raw.is_none() {
        return Ok(());
    }
    if crate::trade_amount_resolution::optional_mint_pair_matches_payload_order(
        input.base_token_mint,
        input.quote_token_mint,
        payload_token_a_mint.as_str(),
        payload_token_b_mint.as_str(),
    ) {
        if base_amount_raw.is_none() {
            *base_amount_raw = payload_order.base_amount_raw;
        }
        if quote_amount_raw.is_none() {
            *quote_amount_raw = payload_order.quote_amount_raw;
        }
        if resolved_trade_side.is_none() {
            *resolved_trade_side = payload_order.resolved_trade_side;
        }
        tracing::debug!(
            event_kind = %input.decoded_event.event_kind,
            decoded_event_id = ?input.decoded_event.id,
            transaction_signature = %input.transaction.signature,
            pool_account = ?input.decoded_event.pool_account,
            payload_token_a_mint = %payload_token_a_mint,
            payload_token_b_mint = %payload_token_b_mint,
            payload_base_vault = %payload_base_vault,
            payload_quote_vault = %payload_quote_vault,
            base_amount_raw = ?base_amount_raw,
            quote_amount_raw = ?quote_amount_raw,
            resolved_trade_side = ?resolved_trade_side,
            "meteora_dbc trade amounts recovered from payload-token CPI transfer window"
        );
        return Ok(());
    }
    if crate::trade_amount_resolution::optional_mint_pair_matches_payload_reverse_order(
        input.base_token_mint,
        input.quote_token_mint,
        payload_token_a_mint.as_str(),
        payload_token_b_mint.as_str(),
    ) {
        if base_amount_raw.is_none() {
            *base_amount_raw = payload_order.quote_amount_raw;
        }
        if quote_amount_raw.is_none() {
            *quote_amount_raw = payload_order.base_amount_raw;
        }
        if resolved_trade_side.is_none() {
            *resolved_trade_side = crate::trade_amount_resolution::reverse_swap_trade_side(
                payload_order.resolved_trade_side,
            );
        }
        tracing::debug!(
            event_kind = %input.decoded_event.event_kind,
            decoded_event_id = ?input.decoded_event.id,
            transaction_signature = %input.transaction.signature,
            pool_account = ?input.decoded_event.pool_account,
            payload_token_a_mint = %payload_token_a_mint,
            payload_token_b_mint = %payload_token_b_mint,
            payload_base_vault = %payload_base_vault,
            payload_quote_vault = %payload_quote_vault,
            base_amount_raw = ?base_amount_raw,
            quote_amount_raw = ?quote_amount_raw,
            resolved_trade_side = ?resolved_trade_side,
            "meteora_dbc trade amounts recovered from reversed payload-token CPI transfer window"
        );
    }
    return Ok(());
}

async fn apply_flattened_cpi_amount_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    protocol_label: &str,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) -> Result<(), crate::Error> {
    let decoded_instruction_result = crate::trade_amount_resolution::load_decoded_instruction(
        input.database,
        input.decoded_event,
    )
    .await;
    let decoded_instruction = match decoded_instruction_result {
        Ok(Some(decoded_instruction)) => decoded_instruction,
        Ok(None) => return Ok(()),
        Err(error) => return Err(error),
    };
    let instructions_result = crate::query_chain_instructions_list_by_transaction_id(
        input.database,
        input.decoded_event.transaction_id,
    )
    .await;
    let instructions = match instructions_result {
        Ok(instructions) => instructions,
        Err(error) => return Err(error),
    };
    let flattened_result = resolve_amounts_from_flattened_cpi_transfer_window(
        &decoded_instruction,
        &instructions,
        input.base_token_mint,
        input.quote_token_mint,
        input.base_vault_address,
        input.quote_vault_address,
    );
    let flattened = match flattened_result {
        Ok(flattened) => flattened,
        Err(error) => return Err(error),
    };
    if base_amount_raw.is_none() {
        *base_amount_raw = flattened.base_amount_raw;
    }
    if quote_amount_raw.is_none() {
        *quote_amount_raw = flattened.quote_amount_raw;
    }
    if resolved_trade_side.is_none() {
        *resolved_trade_side = flattened.resolved_trade_side;
    }
    if base_amount_raw.is_some() || quote_amount_raw.is_some() {
        tracing::debug!(
            event_kind = %input.decoded_event.event_kind,
            pool_account = ?input.decoded_event.pool_account,
            protocol_label = %protocol_label,
            decoded_event_id = ?input.decoded_event.id,
            transaction_signature = %input.transaction.signature,
            base_mint = ?input.base_token_mint,
            quote_mint = ?input.quote_token_mint,
            base_amount_raw = ?base_amount_raw,
            quote_amount_raw = ?quote_amount_raw,
            resolved_trade_side = ?resolved_trade_side,
            "trade amounts recovered from flattened CPI transfer window"
        );
    }
    return Ok(());
}

fn resolve_amounts_from_flattened_cpi_transfer_window(
    decoded_instruction: &crate::ChainInstructionDto,
    transaction_instructions: &[crate::ChainInstructionDto],
    base_token_mint: std::option::Option<&str>,
    quote_token_mint: std::option::Option<&str>,
    base_vault_address: std::option::Option<&str>,
    quote_vault_address: std::option::Option<&str>,
) -> Result<crate::trade_amount_resolution::FlattenedCpiTransferAmountResolution, crate::Error> {
    let decoded_stack_height = match decoded_instruction.stack_height {
        Some(stack_height) => stack_height,
        None => return Ok(crate::trade_amount_resolution::empty_flattened_cpi_amount_resolution()),
    };
    let stop_inner_instruction_index =
        crate::trade_amount_resolution::find_flattened_cpi_window_stop_inner_instruction_index(
            decoded_instruction,
            transaction_instructions,
            decoded_stack_height,
        );
    let mut base_transfer_direction = None;
    let mut quote_transfer_direction = None;
    let mut base_amount_raw = None;
    let mut quote_amount_raw = None;
    for instruction in transaction_instructions {
        if !crate::trade_amount_resolution::instruction_is_inside_flattened_cpi_window(
            decoded_instruction,
            instruction,
            decoded_stack_height,
            stop_inner_instruction_index,
        ) {
            continue;
        }
        let parsed_transfer_result =
            crate::trade_amount_resolution::parse_transfer_checked_instruction(instruction);
        let parsed_transfer = match parsed_transfer_result {
            Ok(Some(parsed_transfer)) => parsed_transfer,
            Ok(None) => continue,
            Err(error) => return Err(error),
        };
        if base_amount_raw.is_none()
            && crate::trade_amount_resolution::string_option_equals(
                base_token_mint,
                parsed_transfer.mint.as_str(),
            )
            && crate::trade_amount_resolution::transfer_touches_vault(
                &parsed_transfer,
                base_vault_address,
            )
        {
            base_transfer_direction = crate::trade_amount_resolution::infer_transfer_direction(
                &parsed_transfer,
                base_vault_address,
            );
            base_amount_raw = Some(parsed_transfer.amount_raw.clone());
            continue;
        }
        if quote_amount_raw.is_none()
            && crate::trade_amount_resolution::string_option_equals(
                quote_token_mint,
                parsed_transfer.mint.as_str(),
            )
            && crate::trade_amount_resolution::transfer_touches_vault(
                &parsed_transfer,
                quote_vault_address,
            )
        {
            quote_transfer_direction = crate::trade_amount_resolution::infer_transfer_direction(
                &parsed_transfer,
                quote_vault_address,
            );
            quote_amount_raw = Some(parsed_transfer.amount_raw.clone());
            continue;
        }
    }
    let resolved_trade_side =
        crate::trade_amount_resolution::infer_trade_side_from_transfer_directions(
            base_transfer_direction,
            quote_transfer_direction,
        );
    return Ok(crate::trade_amount_resolution::FlattenedCpiTransferAmountResolution {
        base_amount_raw,
        quote_amount_raw,
        resolved_trade_side,
    });
}

fn instruction_is_inside_flattened_cpi_window(
    decoded_instruction: &crate::ChainInstructionDto,
    candidate_instruction: &crate::ChainInstructionDto,
    decoded_stack_height: u32,
    stop_inner_instruction_index: std::option::Option<u32>,
) -> bool {
    if candidate_instruction.transaction_id != decoded_instruction.transaction_id {
        return false;
    }
    if candidate_instruction.instruction_index != decoded_instruction.instruction_index {
        return false;
    }
    let candidate_inner_instruction_index = match candidate_instruction.inner_instruction_index {
        Some(inner_instruction_index) => inner_instruction_index,
        None => return false,
    };
    if let Some(decoded_inner_instruction_index) = decoded_instruction.inner_instruction_index {
        if candidate_inner_instruction_index <= decoded_inner_instruction_index {
            return false;
        }
    }
    let candidate_stack_height = match candidate_instruction.stack_height {
        Some(stack_height) => stack_height,
        None => return false,
    };
    if candidate_stack_height <= decoded_stack_height {
        return false;
    }
    if let Some(stop_inner_instruction_index) = stop_inner_instruction_index {
        if candidate_inner_instruction_index >= stop_inner_instruction_index {
            return false;
        }
    }
    return true;
}

fn find_flattened_cpi_window_stop_inner_instruction_index(
    decoded_instruction: &crate::ChainInstructionDto,
    transaction_instructions: &[crate::ChainInstructionDto],
    decoded_stack_height: u32,
) -> std::option::Option<u32> {
    let decoded_inner_instruction_index = match decoded_instruction.inner_instruction_index {
        Some(decoded_inner_instruction_index) => decoded_inner_instruction_index,
        None => return None,
    };
    let mut stop_inner_instruction_index = None;
    for instruction in transaction_instructions {
        if instruction.transaction_id != decoded_instruction.transaction_id {
            continue;
        }
        if instruction.instruction_index != decoded_instruction.instruction_index {
            continue;
        }
        let candidate_inner_instruction_index = match instruction.inner_instruction_index {
            Some(candidate_inner_instruction_index) => candidate_inner_instruction_index,
            None => continue,
        };
        if candidate_inner_instruction_index <= decoded_inner_instruction_index {
            continue;
        }
        let candidate_stack_height = match instruction.stack_height {
            Some(candidate_stack_height) => candidate_stack_height,
            None => continue,
        };
        if candidate_stack_height > decoded_stack_height {
            continue;
        }
        match stop_inner_instruction_index {
            Some(current_stop) => {
                if candidate_inner_instruction_index < current_stop {
                    stop_inner_instruction_index = Some(candidate_inner_instruction_index);
                }
            },
            None => stop_inner_instruction_index = Some(candidate_inner_instruction_index),
        }
    }
    return stop_inner_instruction_index;
}

#[derive(Debug, Clone)]
struct ParsedTransferCheckedInstruction {
    mint: std::string::String,
    amount_raw: std::string::String,
    source: std::string::String,
    destination: std::string::String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum VaultTransferDirection {
    IntoVault,
    OutOfVault,
}

fn parse_transfer_checked_instruction(
    instruction: &crate::ChainInstructionDto,
) -> Result<
    std::option::Option<crate::trade_amount_resolution::ParsedTransferCheckedInstruction>,
    crate::Error,
> {
    let parsed_type = match instruction.parsed_type.as_deref() {
        Some(parsed_type) => parsed_type,
        None => return Ok(None),
    };
    if parsed_type != "transferChecked" {
        return Ok(None);
    }
    let parsed_json_text = match instruction.parsed_json.as_deref() {
        Some(parsed_json_text) => parsed_json_text,
        None => return Ok(None),
    };
    let parsed_json_result = serde_json::from_str::<serde_json::Value>(parsed_json_text);
    let parsed_json = match parsed_json_result {
        Ok(parsed_json) => parsed_json,
        Err(error) => {
            return Err(crate::Error::Json(format!(
                "cannot parse parsed_json for transferChecked instruction '{}': {}",
                crate::trade_amount_resolution::format_instruction_id_for_log(instruction.id),
                error
            )));
        },
    };
    let info = match parsed_json.get("info") {
        Some(info) => info,
        None => return Ok(None),
    };
    let mint =
        match crate::trade_amount_resolution::extract_string_by_candidate_keys(info, &["mint"]) {
            Some(mint) => mint,
            None => return Ok(None),
        };
    let amount_raw =
        match crate::trade_amount_resolution::extract_scalar_as_string_by_candidate_keys(
            info,
            &["amount"],
        ) {
            Some(amount_raw) => amount_raw,
            None => {
                let token_amount = match info.get("tokenAmount") {
                    Some(token_amount) => token_amount,
                    None => return Ok(None),
                };
                match crate::trade_amount_resolution::extract_scalar_as_string_by_candidate_keys(
                    token_amount,
                    &["amount"],
                ) {
                    Some(amount_raw) => amount_raw,
                    None => return Ok(None),
                }
            },
        };
    let source =
        match crate::trade_amount_resolution::extract_string_by_candidate_keys(info, &["source"]) {
            Some(source) => source,
            None => return Ok(None),
        };
    let destination = match crate::trade_amount_resolution::extract_string_by_candidate_keys(
        info,
        &["destination"],
    ) {
        Some(destination) => destination,
        None => return Ok(None),
    };
    return Ok(Some(crate::trade_amount_resolution::ParsedTransferCheckedInstruction {
        mint,
        amount_raw,
        source,
        destination,
    }));
}

fn transfer_touches_vault(
    transfer: &crate::trade_amount_resolution::ParsedTransferCheckedInstruction,
    vault_address: std::option::Option<&str>,
) -> bool {
    let vault_address = match vault_address {
        Some(vault_address) => vault_address,
        None => return true,
    };
    if crate::trade_amount_resolution::account_equals(transfer.source.as_str(), vault_address) {
        return true;
    }
    if crate::trade_amount_resolution::account_equals(transfer.destination.as_str(), vault_address)
    {
        return true;
    }
    return false;
}

fn infer_transfer_direction(
    transfer: &crate::trade_amount_resolution::ParsedTransferCheckedInstruction,
    vault_address: std::option::Option<&str>,
) -> std::option::Option<crate::trade_amount_resolution::VaultTransferDirection> {
    let vault_address = match vault_address {
        Some(vault_address) => vault_address,
        None => return None,
    };
    if crate::trade_amount_resolution::account_equals(transfer.destination.as_str(), vault_address)
    {
        return Some(crate::trade_amount_resolution::VaultTransferDirection::IntoVault);
    }
    if crate::trade_amount_resolution::account_equals(transfer.source.as_str(), vault_address) {
        return Some(crate::trade_amount_resolution::VaultTransferDirection::OutOfVault);
    }
    return None;
}

fn infer_trade_side_from_transfer_directions(
    base_transfer_direction: std::option::Option<
        crate::trade_amount_resolution::VaultTransferDirection,
    >,
    quote_transfer_direction: std::option::Option<
        crate::trade_amount_resolution::VaultTransferDirection,
    >,
) -> std::option::Option<crate::SwapTradeSide> {
    match (base_transfer_direction, quote_transfer_direction) {
        (
            Some(crate::trade_amount_resolution::VaultTransferDirection::OutOfVault),
            Some(crate::trade_amount_resolution::VaultTransferDirection::IntoVault),
        ) => return Some(crate::SwapTradeSide::BuyBase),
        (
            Some(crate::trade_amount_resolution::VaultTransferDirection::IntoVault),
            Some(crate::trade_amount_resolution::VaultTransferDirection::OutOfVault),
        ) => return Some(crate::SwapTradeSide::SellBase),
        _ => return None,
    }
}

fn optional_mint_pair_matches_payload_order(
    base_token_mint: std::option::Option<&str>,
    quote_token_mint: std::option::Option<&str>,
    payload_token_a_mint: &str,
    payload_token_b_mint: &str,
) -> bool {
    return crate::trade_amount_resolution::string_option_equals(
        base_token_mint,
        payload_token_a_mint,
    ) && crate::trade_amount_resolution::string_option_equals(quote_token_mint, payload_token_b_mint);
}

fn optional_mint_pair_matches_payload_reverse_order(
    base_token_mint: std::option::Option<&str>,
    quote_token_mint: std::option::Option<&str>,
    payload_token_a_mint: &str,
    payload_token_b_mint: &str,
) -> bool {
    return crate::trade_amount_resolution::string_option_equals(
        base_token_mint,
        payload_token_b_mint,
    ) && crate::trade_amount_resolution::string_option_equals(quote_token_mint, payload_token_a_mint);
}

fn reverse_swap_trade_side(
    trade_side: std::option::Option<crate::SwapTradeSide>,
) -> std::option::Option<crate::SwapTradeSide> {
    match trade_side {
        Some(crate::SwapTradeSide::BuyBase) => return Some(crate::SwapTradeSide::SellBase),
        Some(crate::SwapTradeSide::SellBase) => return Some(crate::SwapTradeSide::BuyBase),
        Some(crate::SwapTradeSide::Unknown) => return Some(crate::SwapTradeSide::Unknown),
        None => return None,
    }
}

fn string_option_equals(left: std::option::Option<&str>, right: &str) -> bool {
    let left = match left {
        Some(left) => left.trim(),
        None => return false,
    };
    let right = right.trim();
    if left.is_empty() || right.is_empty() {
        return false;
    }
    return left == right;
}

fn account_equals(left: &str, right: &str) -> bool {
    let left = left.trim();
    let right = right.trim();
    if left.is_empty() || right.is_empty() {
        return false;
    }
    return left == right;
}

fn format_instruction_id_for_log(instruction_id: std::option::Option<i64>) -> std::string::String {
    match instruction_id {
        Some(instruction_id) => return instruction_id.to_string(),
        None => return "<none>".to_string(),
    }
}

fn empty_flattened_cpi_amount_resolution()
-> crate::trade_amount_resolution::FlattenedCpiTransferAmountResolution {
    return crate::trade_amount_resolution::FlattenedCpiTransferAmountResolution {
        base_amount_raw: None,
        quote_amount_raw: None,
        resolved_trade_side: None,
    };
}

fn apply_vault_balance_delta_fallback(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_vault_address: std::option::Option<&str>,
    quote_vault_address: std::option::Option<&str>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    price_quote_per_base: &mut std::option::Option<f64>,
) -> Result<(), crate::Error> {
    let inferred_result =
        crate::trade_solana_amounts::extract_trade_amounts_from_vault_balance_deltas(
            input.transaction.transaction_json.as_str(),
            input.transaction.meta_json.as_deref(),
            base_vault_address,
            quote_vault_address,
        );
    let inferred = match inferred_result {
        Ok(inferred) => inferred,
        Err(error) => return Err(error),
    };
    if base_amount_raw.is_none() {
        *base_amount_raw = inferred.0;
    }
    if quote_amount_raw.is_none() {
        *quote_amount_raw = inferred.1;
    }
    if price_quote_per_base.is_none() {
        *price_quote_per_base = inferred.2;
    }
    return Ok(());
}

async fn load_decoded_instruction_index(
    database: &crate::Database,
    decoded_event: &crate::DexDecodedEventDto,
) -> Result<std::option::Option<u32>, crate::Error> {
    let instruction_result =
        crate::trade_amount_resolution::load_decoded_instruction(database, decoded_event).await;
    let instruction_option = match instruction_result {
        Ok(instruction_option) => instruction_option,
        Err(error) => return Err(error),
    };
    match instruction_option {
        Some(instruction) => return Ok(Some(instruction.instruction_index)),
        None => return Ok(None),
    }
}

async fn load_decoded_instruction(
    database: &crate::Database,
    decoded_event: &crate::DexDecodedEventDto,
) -> Result<std::option::Option<crate::ChainInstructionDto>, crate::Error> {
    let instruction_id = match decoded_event.instruction_id {
        Some(instruction_id) => instruction_id,
        None => return Ok(None),
    };
    let instruction_result =
        crate::query_chain_instructions_get_by_id(database, instruction_id).await;
    let instruction_option = match instruction_result {
        Ok(instruction_option) => instruction_option,
        Err(error) => return Err(error),
    };
    return Ok(instruction_option);
}

fn normalize_pump_swap_anchor_buy_exact_quote_in_amounts(
    input: &crate::trade_amount_resolution::TradeAmountResolutionInput<'_>,
    base_amount_raw: &mut std::option::Option<std::string::String>,
    quote_amount_raw: &mut std::option::Option<std::string::String>,
    resolved_trade_side: &mut std::option::Option<crate::SwapTradeSide>,
) {
    let amount_source = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["amountSource", "amount_source"],
    );
    if amount_source.as_deref() != Some("pump_swap_anchor_buy_event") {
        return;
    }
    let token_a_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["tokenAMint", "token_a_mint"],
    );
    let token_b_mint = crate::trade_amount_resolution::extract_string_by_candidate_keys(
        input.payload,
        &["tokenBMint", "token_b_mint"],
    );
    let token_a_mint = match token_a_mint.as_deref() {
        Some(token_a_mint) => token_a_mint,
        None => return,
    };
    let token_b_mint = match token_b_mint.as_deref() {
        Some(token_b_mint) => token_b_mint,
        None => return,
    };
    let pair_base_mint = match input.base_token_mint {
        Some(pair_base_mint) => pair_base_mint,
        None => return,
    };
    let pair_quote_mint = match input.quote_token_mint {
        Some(pair_quote_mint) => pair_quote_mint,
        None => return,
    };
    let anchor_base_amount_raw = crate::trade_amount_resolution::extract_amount_string(
        input.payload,
        &["baseAmountRaw", "baseAmountOutRaw", "base_amount_out_raw"],
    );
    let anchor_quote_amount_raw = crate::trade_amount_resolution::extract_amount_string(
        input.payload,
        &["quoteAmountRaw", "userQuoteAmountInRaw", "user_quote_amount_in_raw"],
    );
    let anchor_base_amount_raw = match anchor_base_amount_raw {
        Some(anchor_base_amount_raw) => anchor_base_amount_raw,
        None => return,
    };
    let anchor_quote_amount_raw = match anchor_quote_amount_raw {
        Some(anchor_quote_amount_raw) => anchor_quote_amount_raw,
        None => return,
    };
    if pair_base_mint == token_a_mint && pair_quote_mint == token_b_mint {
        *base_amount_raw = Some(anchor_base_amount_raw);
        *quote_amount_raw = Some(anchor_quote_amount_raw);
        *resolved_trade_side = Some(crate::SwapTradeSide::BuyBase);
        return;
    }
    if pair_base_mint == token_b_mint && pair_quote_mint == token_a_mint {
        *base_amount_raw = Some(anchor_quote_amount_raw);
        *quote_amount_raw = Some(anchor_base_amount_raw);
        *resolved_trade_side = Some(crate::SwapTradeSide::SellBase);
        return;
    }
}

fn extract_amount_string(
    payload: &serde_json::Value,
    candidate_keys: &[&str],
) -> std::option::Option<std::string::String> {
    return crate::trade_amount_resolution::extract_scalar_as_string_by_candidate_keys(
        payload,
        candidate_keys,
    );
}

fn extract_bool_by_candidate_keys(
    value: &serde_json::Value,
    candidate_keys: &[&str],
) -> std::option::Option<bool> {
    if let Some(object) = value.as_object() {
        for candidate_key in candidate_keys {
            let direct_option = object.get(*candidate_key);
            if let Some(direct) = direct_option {
                if let Some(bool_value) = direct.as_bool() {
                    return Some(bool_value);
                }
            }
        }
        for nested_value in object.values() {
            let nested_result = crate::trade_amount_resolution::extract_bool_by_candidate_keys(
                nested_value,
                candidate_keys,
            );
            if nested_result.is_some() {
                return nested_result;
            }
        }
        return None;
    }
    if let Some(array) = value.as_array() {
        for nested_value in array {
            let nested_result = crate::trade_amount_resolution::extract_bool_by_candidate_keys(
                nested_value,
                candidate_keys,
            );
            if nested_result.is_some() {
                return nested_result;
            }
        }
    }
    return None;
}

fn extract_string_by_candidate_keys(
    value: &serde_json::Value,
    candidate_keys: &[&str],
) -> std::option::Option<std::string::String> {
    if let Some(object) = value.as_object() {
        for candidate_key in candidate_keys {
            let direct_option = object.get(*candidate_key);
            if let Some(direct) = direct_option {
                let direct_text_option = direct.as_str();
                if let Some(direct_text) = direct_text_option {
                    return Some(direct_text.to_string());
                }
            }
        }
        for nested_value in object.values() {
            let nested_result = crate::trade_amount_resolution::extract_string_by_candidate_keys(
                nested_value,
                candidate_keys,
            );
            if nested_result.is_some() {
                return nested_result;
            }
        }
        return None;
    }
    if let Some(array) = value.as_array() {
        for nested_value in array {
            let nested_result = crate::trade_amount_resolution::extract_string_by_candidate_keys(
                nested_value,
                candidate_keys,
            );
            if nested_result.is_some() {
                return nested_result;
            }
        }
    }
    return None;
}

fn extract_scalar_as_string_by_candidate_keys(
    value: &serde_json::Value,
    candidate_keys: &[&str],
) -> std::option::Option<std::string::String> {
    if let Some(object) = value.as_object() {
        for candidate_key in candidate_keys {
            let direct_option = object.get(*candidate_key);
            if let Some(direct) = direct_option {
                if let Some(text) = direct.as_str() {
                    return Some(text.to_string());
                }
                if let Some(number) = direct.as_i64() {
                    return Some(number.to_string());
                }
                if let Some(number) = direct.as_u64() {
                    return Some(number.to_string());
                }
                if let Some(number) = direct.as_f64() {
                    return Some(number.to_string());
                }
            }
        }
        for nested_value in object.values() {
            let nested_result =
                crate::trade_amount_resolution::extract_scalar_as_string_by_candidate_keys(
                    nested_value,
                    candidate_keys,
                );
            if nested_result.is_some() {
                return nested_result;
            }
        }
        return None;
    }
    if let Some(array) = value.as_array() {
        for nested_value in array {
            let nested_result =
                crate::trade_amount_resolution::extract_scalar_as_string_by_candidate_keys(
                    nested_value,
                    candidate_keys,
                );
            if nested_result.is_some() {
                return nested_result;
            }
        }
    }
    return None;
}

fn infer_trade_side_from_vault_balance_deltas(
    meta_json: std::option::Option<&str>,
    transaction_json: &str,
    base_vault_address: std::option::Option<&str>,
    quote_vault_address: std::option::Option<&str>,
) -> std::option::Option<crate::SwapTradeSide> {
    let base_vault_address = match base_vault_address {
        Some(base_vault_address) => base_vault_address,
        None => return None,
    };
    let quote_vault_address = match quote_vault_address {
        Some(quote_vault_address) => quote_vault_address,
        None => return None,
    };
    let meta_text = match meta_json {
        Some(meta_text) => meta_text,
        None => return None,
    };
    let meta_result = serde_json::from_str::<serde_json::Value>(meta_text);
    let meta = match meta_result {
        Ok(meta) => meta,
        Err(_) => return None,
    };
    let transaction_result = serde_json::from_str::<serde_json::Value>(transaction_json);
    let transaction = match transaction_result {
        Ok(transaction) => transaction,
        Err(_) => return None,
    };
    if transaction_failed(&meta) {
        return None;
    }
    let account_keys = collect_transaction_account_keys(&transaction, &meta);
    let base_account_index = find_account_index(&account_keys, base_vault_address);
    let base_account_index = match base_account_index {
        Some(base_account_index) => base_account_index,
        None => return None,
    };
    let quote_account_index = find_account_index(&account_keys, quote_vault_address);
    let quote_account_index = match quote_account_index {
        Some(quote_account_index) => quote_account_index,
        None => return None,
    };
    let base_delta = token_balance_delta_for_account_index(&meta, base_account_index);
    let base_delta = match base_delta {
        Some(base_delta) => base_delta,
        None => return None,
    };
    let quote_delta = token_balance_delta_for_account_index(&meta, quote_account_index);
    let quote_delta = match quote_delta {
        Some(quote_delta) => quote_delta,
        None => return None,
    };
    if base_delta < 0_i128 && quote_delta > 0_i128 {
        return Some(crate::SwapTradeSide::BuyBase);
    }
    if base_delta > 0_i128 && quote_delta < 0_i128 {
        return Some(crate::SwapTradeSide::SellBase);
    }
    return None;
}

fn transaction_failed(meta: &serde_json::Value) -> bool {
    if let Some(err_value) = meta.get("err") {
        if !err_value.is_null() {
            return true;
        }
    }
    if let Some(status) = meta.get("status") {
        if let Some(object) = status.as_object() {
            if object.get("Err").is_some() {
                return true;
            }
        }
    }
    return false;
}

fn collect_transaction_account_keys(
    transaction: &serde_json::Value,
    meta: &serde_json::Value,
) -> std::vec::Vec<std::string::String> {
    let mut account_keys = std::vec::Vec::new();
    collect_account_keys_from_candidate_path(
        transaction,
        &["transaction", "message", "accountKeys"],
        &mut account_keys,
    );
    if account_keys.is_empty() {
        collect_account_keys_from_candidate_path(
            transaction,
            &["message", "accountKeys"],
            &mut account_keys,
        );
    }
    collect_loaded_addresses(meta, &mut account_keys);
    return account_keys;
}

fn collect_account_keys_from_candidate_path(
    value: &serde_json::Value,
    path: &[&str],
    target: &mut std::vec::Vec<std::string::String>,
) {
    let mut current = value;
    for key in path {
        let next = current.get(*key);
        current = match next {
            Some(next) => next,
            None => return,
        };
    }
    let array = match current.as_array() {
        Some(array) => array,
        None => return,
    };
    for item in array {
        if let Some(text) = item.as_str() {
            target.push(text.to_string());
            continue;
        }
        if let Some(pubkey) = item.get("pubkey").and_then(|value| return value.as_str()) {
            target.push(pubkey.to_string());
        }
    }
}

fn collect_loaded_addresses(
    meta: &serde_json::Value,
    target: &mut std::vec::Vec<std::string::String>,
) {
    let loaded_addresses = match meta.get("loadedAddresses") {
        Some(loaded_addresses) => loaded_addresses,
        None => return,
    };
    collect_loaded_address_array(loaded_addresses, "writable", target);
    collect_loaded_address_array(loaded_addresses, "readonly", target);
}

fn collect_loaded_address_array(
    loaded_addresses: &serde_json::Value,
    key: &str,
    target: &mut std::vec::Vec<std::string::String>,
) {
    let array = match loaded_addresses.get(key).and_then(|value| return value.as_array()) {
        Some(array) => array,
        None => return,
    };
    for item in array {
        if let Some(text) = item.as_str() {
            target.push(text.to_string());
        }
    }
}

fn find_account_index(
    account_keys: &[std::string::String],
    account_address: &str,
) -> std::option::Option<usize> {
    for (index, account_key) in account_keys.iter().enumerate() {
        if account_key == account_address {
            return Some(index);
        }
    }
    return None;
}

fn token_balance_delta_for_account_index(
    meta: &serde_json::Value,
    account_index: usize,
) -> std::option::Option<i128> {
    let pre_amount =
        token_balance_amount_for_account_index(meta, "preTokenBalances", account_index);
    let pre_amount = match pre_amount {
        Some(pre_amount) => pre_amount,
        None => return None,
    };
    let post_amount =
        token_balance_amount_for_account_index(meta, "postTokenBalances", account_index);
    let post_amount = match post_amount {
        Some(post_amount) => post_amount,
        None => return None,
    };
    return Some(post_amount - pre_amount);
}

fn token_balance_amount_for_account_index(
    meta: &serde_json::Value,
    key: &str,
    account_index: usize,
) -> std::option::Option<i128> {
    let balances = match meta.get(key).and_then(|value| return value.as_array()) {
        Some(balances) => balances,
        None => return None,
    };
    for balance in balances {
        let balance_index = balance.get("accountIndex").and_then(|value| return value.as_u64());
        let balance_index = match balance_index {
            Some(balance_index) => balance_index,
            None => continue,
        };
        let requested_index = match u64::try_from(account_index) {
            Ok(requested_index) => requested_index,
            Err(_) => return None,
        };
        if balance_index != requested_index {
            continue;
        }
        let amount_text = balance
            .get("uiTokenAmount")
            .and_then(|value| return value.get("amount"))
            .and_then(|value| return value.as_str());
        let amount_text = match amount_text {
            Some(amount_text) => amount_text,
            None => return None,
        };
        let amount_result = amount_text.parse::<i128>();
        match amount_result {
            Ok(amount) => return Some(amount),
            Err(_) => return None,
        }
    }
    return None;
}

#[cfg(test)]
mod tests {
    fn transaction_json_with_vaults(base_vault: &str, quote_vault: &str) -> std::string::String {
        return serde_json::json!({
            "transaction": {
                "message": {
                    "accountKeys": [
                        base_vault,
                        quote_vault
                    ]
                }
            }
        })
        .to_string();
    }

    fn meta_json_with_vault_amounts(
        base_pre: &str,
        base_post: &str,
        quote_pre: &str,
        quote_post: &str,
    ) -> std::string::String {
        return serde_json::json!({
            "err": null,
            "status": {
                "Ok": null
            },
            "preTokenBalances": [
                {
                    "accountIndex": 0,
                    "uiTokenAmount": {
                        "amount": base_pre
                    }
                },
                {
                    "accountIndex": 1,
                    "uiTokenAmount": {
                        "amount": quote_pre
                    }
                }
            ],
            "postTokenBalances": [
                {
                    "accountIndex": 0,
                    "uiTokenAmount": {
                        "amount": base_post
                    }
                },
                {
                    "accountIndex": 1,
                    "uiTokenAmount": {
                        "amount": quote_post
                    }
                }
            ]
        })
        .to_string();
    }

    fn make_test_instruction(
        id: i64,
        parent_instruction_id: std::option::Option<i64>,
        instruction_index: u32,
        inner_instruction_index: std::option::Option<u32>,
        stack_height: std::option::Option<u32>,
        parsed_type: std::option::Option<&str>,
        parsed_json: std::option::Option<std::string::String>,
    ) -> crate::ChainInstructionDto {
        return crate::ChainInstructionDto {
            id: Some(id),
            transaction_id: 10,
            parent_instruction_id,
            instruction_index,
            inner_instruction_index,
            program_id: None,
            program_name: None,
            stack_height,
            accounts_json: "[]".to_string(),
            data_json: None,
            parsed_type: parsed_type.map(|value| return value.to_string()),
            parsed_json,
            created_at: chrono::Utc::now(),
        };
    }

    fn transfer_checked_json(
        mint: &str,
        amount: &str,
        source: &str,
        destination: &str,
    ) -> std::string::String {
        return serde_json::json!({
            "info": {
                "mint": mint,
                "source": source,
                "destination": destination,
                "tokenAmount": {
                    "amount": amount
                }
            },
            "type": "transferChecked"
        })
        .to_string();
    }

    #[test]
    fn flattened_cpi_window_extracts_meteora_dlmm_transfer_checked_amounts() {
        let decoded_instruction =
            make_test_instruction(100, Some(90), 3, Some(1), Some(3), None, None);
        let instructions = vec![
            decoded_instruction.clone(),
            make_test_instruction(101, Some(90), 3, Some(2), Some(4), None, None),
            make_test_instruction(
                102,
                Some(90),
                3,
                Some(3),
                Some(4),
                Some("transferChecked"),
                Some(transfer_checked_json("BASE", "5689283022", "UserBase", "BaseVault")),
            ),
            make_test_instruction(
                103,
                Some(90),
                3,
                Some(4),
                Some(4),
                Some("transferChecked"),
                Some(transfer_checked_json("QUOTE", "1322754129", "QuoteVault", "UserQuote")),
            ),
            make_test_instruction(
                104,
                Some(90),
                3,
                Some(5),
                Some(3),
                Some("transferChecked"),
                Some(transfer_checked_json("QUOTE", "999", "QuoteVault", "UserQuote")),
            ),
        ];
        let resolution_result = super::resolve_amounts_from_flattened_cpi_transfer_window(
            &decoded_instruction,
            &instructions,
            Some("BASE"),
            Some("QUOTE"),
            Some("BaseVault"),
            Some("QuoteVault"),
        );
        let resolution = match resolution_result {
            Ok(resolution) => resolution,
            Err(error) => panic!("flattened CPI extraction should succeed: {}", error),
        };
        assert_eq!(resolution.base_amount_raw, Some("5689283022".to_string()));
        assert_eq!(resolution.quote_amount_raw, Some("1322754129".to_string()));
        assert_eq!(resolution.resolved_trade_side, Some(crate::SwapTradeSide::SellBase));
    }

    #[test]
    fn flattened_cpi_window_extracts_top_level_meteora_damm_v1_transfer_checked_amounts() {
        let decoded_instruction = make_test_instruction(200, None, 4, None, Some(1), None, None);
        let instructions = vec![
            decoded_instruction.clone(),
            make_test_instruction(
                201,
                Some(200),
                4,
                Some(0),
                Some(2),
                Some("transferChecked"),
                Some(transfer_checked_json("BASE", "250000000", "UserBase", "PoolBaseVault")),
            ),
            make_test_instruction(
                202,
                Some(200),
                4,
                Some(1),
                Some(2),
                Some("transferChecked"),
                Some(transfer_checked_json("QUOTE", "10000000", "PoolQuoteVault", "UserQuote")),
            ),
        ];
        let resolution_result = super::resolve_amounts_from_flattened_cpi_transfer_window(
            &decoded_instruction,
            &instructions,
            Some("BASE"),
            Some("QUOTE"),
            None,
            None,
        );
        let resolution = match resolution_result {
            Ok(resolution) => resolution,
            Err(error) => panic!("top-level flattened CPI extraction should succeed: {}", error),
        };
        assert_eq!(resolution.base_amount_raw, Some("250000000".to_string()));
        assert_eq!(resolution.quote_amount_raw, Some("10000000".to_string()));
        assert_eq!(resolution.resolved_trade_side, None);
    }

    #[test]
    fn buy_base_is_inferred_when_base_vault_decreases_and_quote_vault_increases() {
        let transaction_json = transaction_json_with_vaults("base_vault", "quote_vault");
        let meta_json = meta_json_with_vault_amounts("1000", "900", "5000", "5200");
        let side = super::infer_trade_side_from_vault_balance_deltas(
            Some(meta_json.as_str()),
            transaction_json.as_str(),
            Some("base_vault"),
            Some("quote_vault"),
        );
        assert_eq!(side, Some(crate::SwapTradeSide::BuyBase));
    }

    #[test]
    fn sell_base_is_inferred_when_base_vault_increases_and_quote_vault_decreases() {
        let transaction_json = transaction_json_with_vaults("base_vault", "quote_vault");
        let meta_json = meta_json_with_vault_amounts("1000", "1200", "5000", "4900");
        let side = super::infer_trade_side_from_vault_balance_deltas(
            Some(meta_json.as_str()),
            transaction_json.as_str(),
            Some("base_vault"),
            Some("quote_vault"),
        );
        assert_eq!(side, Some(crate::SwapTradeSide::SellBase));
    }

    #[test]
    fn raydium_launchpad_buy_instruction_maps_amount_in_out_to_base_quote() {
        let mut base_amount_raw = None;
        let mut quote_amount_raw = None;
        let mut resolved_trade_side = None;
        super::apply_raydium_launchpad_side_amount_mapping(
            crate::SwapTradeSide::BuyBase,
            "15000",
            "262584872",
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut resolved_trade_side,
        );
        assert_eq!(base_amount_raw, Some("262584872".to_string()));
        assert_eq!(quote_amount_raw, Some("15000".to_string()));
        assert_eq!(resolved_trade_side, Some(crate::SwapTradeSide::BuyBase));
    }

    #[test]
    fn raydium_launchpad_sell_instruction_maps_amount_in_out_to_base_quote() {
        let mut base_amount_raw = None;
        let mut quote_amount_raw = None;
        let mut resolved_trade_side = None;
        super::apply_raydium_launchpad_side_amount_mapping(
            crate::SwapTradeSide::SellBase,
            "11093407717995",
            "43968050",
            &mut base_amount_raw,
            &mut quote_amount_raw,
            &mut resolved_trade_side,
        );
        assert_eq!(base_amount_raw, Some("11093407717995".to_string()));
        assert_eq!(quote_amount_raw, Some("43968050".to_string()));
        assert_eq!(resolved_trade_side, Some(crate::SwapTradeSide::SellBase));
    }

    #[test]
    fn raydium_launchpad_instruction_side_maps_exact_in_and_exact_out_variants() {
        assert_eq!(
            super::raydium_launchpad_instruction_side("raydium_launchpad.buy_exact_in"),
            Some(crate::SwapTradeSide::BuyBase)
        );
        assert_eq!(
            super::raydium_launchpad_instruction_side("raydium_launchpad.buy_exact_out"),
            Some(crate::SwapTradeSide::BuyBase)
        );
        assert_eq!(
            super::raydium_launchpad_instruction_side("raydium_launchpad.sell_exact_in"),
            Some(crate::SwapTradeSide::SellBase)
        );
        assert_eq!(
            super::raydium_launchpad_instruction_side("raydium_launchpad.sell_exact_out"),
            Some(crate::SwapTradeSide::SellBase)
        );
        assert_eq!(
            super::raydium_launchpad_instruction_side("raydium_launchpad.collect_fee"),
            None
        );
    }

    #[test]
    fn failed_transaction_does_not_infer_trade_side() {
        let transaction_json = transaction_json_with_vaults("base_vault", "quote_vault");
        let meta_json = serde_json::json!({
            "err": {
                "InstructionError": [3, {"Custom": 104}]
            },
            "status": {
                "Err": {
                    "InstructionError": [3, {"Custom": 104}]
                }
            },
            "preTokenBalances": [],
            "postTokenBalances": []
        })
        .to_string();
        let side = super::infer_trade_side_from_vault_balance_deltas(
            Some(meta_json.as_str()),
            transaction_json.as_str(),
            Some("base_vault"),
            Some("quote_vault"),
        );
        assert_eq!(side, None);
    }
}
