The logic: the instantiate/execute/query entry points and every handler behind them. This is where the rules actually run.
Live source from contracts/amm/src/contract.rs (2086 lines). Generated from the deployed contract code.
use cosmwasm_std::{
    entry_point, to_binary, Addr, BankMsg, Binary, Coin,
    Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128, StdError, WasmMsg,
    Order,
};
use cw2::set_contract_version;
use cw20::{Cw20ReceiveMsg, TokenInfoResponse};
use cw_storage_plus::{Bound, Item};

use crate::error::ContractError;
use crate::msg::{
    ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, SwapTokenForBwick, PoolResponse,
    AllPoolsResponse, SimulateSwapResponse, ConfigResponse, AugmentedFeeStatusResponse,
    LpBalanceResponse, SimulateAddLiquidityResponse,
};
use crate::state::{Config, Pool, AugmentedFeeConfig, CONFIG, POOLS, OPEN_LP};

const CONTRACT_NAME: &str = "crates.io:bwick-amm";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
/// Maximum total fee (base + augmented) in basis points: 1000 bps = 10%
const MAX_TOTAL_FEE_BPS: u16 = 1000;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
    deps: DepsMut,
    _env: Env,
    _info: MessageInfo,
    msg: InstantiateMsg,
) -> Result<Response, ContractError> {
    // Validate authorized creators addresses
    let mut authorized_creators = Vec::new();
    for creator_str in msg.authorized_creators.iter() {
        let validated = deps.api.addr_validate(creator_str)?;
        authorized_creators.push(validated);
    }

    // Validate swap_fee_bps against fee cap
    // Base fee alone cannot exceed the total fee cap since augmented fees add on top
    if msg.swap_fee_bps > MAX_TOTAL_FEE_BPS {
        return Err(ContractError::TotalFeeCapExceeded {
            swap_fee_bps: msg.swap_fee_bps,
            augmented_fee_bps: 0,
            total: msg.swap_fee_bps,
        });
    }

    // Save config
    let config = Config {
        authorized_creators: authorized_creators.clone(),
        swap_fee_bps: msg.swap_fee_bps,
        max_wallet_bps: msg.max_wallet_bps,
    };
    CONFIG.save(deps.storage, &config)?;

    // Set contract version
    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

    // Create comma-separated list for attribute
    let creators_list = authorized_creators
        .iter()
        .map(|a| a.to_string())
        .collect::<Vec<_>>()
        .join(",");

    Ok(Response::new()
        .add_attribute("action", "instantiate")
        .add_attribute("authorized_creators", creators_list)
        .add_attribute("swap_fee_bps", msg.swap_fee_bps.to_string()))
}

// Legacy config format for migration compatibility
#[cosmwasm_schema::cw_serde]
struct LegacyConfig {
    pub bonding_curve_contract: Addr,
    pub swap_fee_bps: u16,
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
    deps: DepsMut,
    _env: Env,
    msg: MigrateMsg,
) -> Result<Response, ContractError> {
    // Try loading as new format first, fall back to legacy
    let mut config = match CONFIG.load(deps.storage) {
        Ok(c) => c,
        Err(_) => {
            // Try legacy format
            let legacy_item: Item<LegacyConfig> = Item::new("config");
            let legacy = legacy_item.load(deps.storage)?;
            let new_config = Config {
                authorized_creators: vec![legacy.bonding_curve_contract],
                swap_fee_bps: legacy.swap_fee_bps,
                max_wallet_bps: 300,
            };
            CONFIG.save(deps.storage, &new_config)?;
            new_config
        }
    };

    // Apply add/remove operations from MigrateMsg
    if let Some(add_addr) = msg.add_authorized_creator {
        let validated = deps.api.addr_validate(&add_addr)?;
        if !config.authorized_creators.contains(&validated) {
            config.authorized_creators.push(validated);
            CONFIG.save(deps.storage, &config)?;
        }
    }

    if let Some(remove_addr) = msg.remove_authorized_creator {
        let validated = deps.api.addr_validate(&remove_addr)?;
        config.authorized_creators.retain(|a| a != &validated);
        CONFIG.save(deps.storage, &config)?;
    }

    if let Some(max_wallet) = msg.max_wallet_bps {
        config.max_wallet_bps = max_wallet;
        CONFIG.save(deps.storage, &config)?;
    }

    // v2: backfill locked_lp_supply for existing pools that were created before this field
    // existed. Treat all pre-existing LP as locked (since open LPs weren't a thing yet).
    let pool_keys: Vec<Addr> = POOLS
        .keys(deps.storage, None, None, Order::Ascending)
        .filter_map(|r| r.ok())
        .collect();
    for k in pool_keys {
        let mut pool = POOLS.load(deps.storage, &k)?;
        if pool.locked_lp_supply == 0 && pool.lp_total_supply > 0 {
            pool.locked_lp_supply = pool.lp_total_supply;
            POOLS.save(deps.storage, &k, &pool)?;
        }
    }

    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
    Ok(Response::new().add_attribute("action", "migrate"))
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> Result<Response, ContractError> {
    match msg {
        ExecuteMsg::CreatePool {
            token_address,
            bwick_amount,
            token_amount,
            augmented_fee_bps,
            lp_target_ubwick,
            locked_lp_unlock_at,
            locked_lp_recipient,
        } => execute_create_pool(
            deps,
            env,
            info,
            token_address,
            bwick_amount,
            token_amount,
            augmented_fee_bps,
            lp_target_ubwick,
            locked_lp_unlock_at,
            locked_lp_recipient,
        ),
        ExecuteMsg::Swap {
            token_address,
            offer_bwick,
            min_output,
        } => execute_swap(deps, env, info, token_address, offer_bwick, min_output),
        ExecuteMsg::AddLiquidity {
            token_address,
            max_token_amount,
        } => execute_add_liquidity(deps, env, info, token_address, max_token_amount),
        ExecuteMsg::RemoveLiquidity {
            token_address,
            lp_amount,
        } => execute_remove_liquidity(deps, env, info, token_address, lp_amount),
        ExecuteMsg::WithdrawLockedLp {
            token_address,
            recipient,
        } => execute_withdraw_locked_lp(deps, env, info, token_address, recipient),
        ExecuteMsg::Receive(cw20_msg) => execute_receive(deps, env, info, cw20_msg),
    }
}

fn execute_create_pool(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    token_address: String,
    bwick_amount: Uint128,
    token_amount: Uint128,
    augmented_fee_bps: Option<u16>,
    lp_target_ubwick: Option<String>,
    locked_lp_unlock_at: Option<u64>,
    locked_lp_recipient: Option<String>,
) -> Result<Response, ContractError> {
    // Authorization check: only authorized creators can create pools
    let config = CONFIG.load(deps.storage)?;
    if !config.authorized_creators.contains(&info.sender) {
        return Err(ContractError::UnauthorizedPoolCreation {});
    }

    // Validate amounts
    if bwick_amount.is_zero() || token_amount.is_zero() {
        return Err(ContractError::ZeroAmount {});
    }

    // Validate token address
    let token_addr = deps.api.addr_validate(&token_address)?;

    // Check if token is valid CW20
    validate_cw20_token(deps.as_ref(), &token_addr)?;

    // Check pool doesn't already exist
    if POOLS.may_load(deps.storage, &token_addr)?.is_some() {
        return Err(ContractError::PoolAlreadyExists {
            token: token_address,
        });
    }

    // Verify BWICK funds sent with message
    let bwick_sent = extract_bwick_from_funds(&info.funds)?;
    if bwick_sent != bwick_amount {
        return Err(ContractError::Std(StdError::generic_err(format!(
            "BWICK amount mismatch: expected {}, got {}",
            bwick_amount, bwick_sent
        ))));
    }

    // Calculate initial LP supply: sqrt(bwick_amount * token_amount)
    let lp_total_supply = integer_sqrt(bwick_amount.u128() * token_amount.u128());

    // Process augmented fee configuration
    let augmented_fee = match (augmented_fee_bps, lp_target_ubwick) {
        (Some(fee_bps), Some(target_str)) => {
            // Both provided - validate and create config
            if fee_bps == 0 || fee_bps > 500 {
                return Err(ContractError::AugmentedFeeInvalid {});
            }
            // Validate total fee cap: base + augmented must not exceed 1000 bps (10%)
            let total_fee = config.swap_fee_bps + fee_bps;
            if total_fee > MAX_TOTAL_FEE_BPS {
                return Err(ContractError::TotalFeeCapExceeded {
                    swap_fee_bps: config.swap_fee_bps,
                    augmented_fee_bps: fee_bps,
                    total: total_fee,
                });
            }
            let target = target_str.parse::<u128>().map_err(|_| {
                ContractError::Std(StdError::generic_err("Invalid lp_target_ubwick format"))
            })?;
            if target == 0 {
                return Err(ContractError::AugmentedFeeInvalid {});
            }
            Some(AugmentedFeeConfig {
                augmented_fee_bps: fee_bps,
                lp_target_ubwick: target,
                active: true,
            })
        }
        (None, None) => None, // Neither provided - normal pool
        _ => return Err(ContractError::AugmentedFeeIncomplete {}), // Only one provided
    };

    // v3: validate lock config. If unlock_at is set, recipient is required.
    // If recipient is set without unlock_at, we silently ignore it (treated
    // as permanent lock) — that keeps the existing callers backwards-compat.
    let (locked_lp_unlock_at_v, locked_lp_recipient_v) = match locked_lp_unlock_at {
        Some(t) => {
            let recipient_str = locked_lp_recipient
                .ok_or(ContractError::LockedLpRecipientRequired {})?;
            let recipient_addr = deps.api.addr_validate(&recipient_str)?;
            (Some(t), Some(recipient_addr))
        }
        None => (None, None),
    };

    // Create pool. The initial seed is fully locked: locked_lp_supply == lp_total_supply.
    // Open LPs added later (via AddLiquidity) are tracked separately in OPEN_LP.
    let pool = Pool {
        token_address: token_addr.clone(),
        bwick_reserve: bwick_amount.u128(),
        token_reserve: token_amount.u128(),
        lp_token_address: env.contract.address.clone(),
        lp_total_supply,
        locked_lp_supply: lp_total_supply,
        locked_lp_unlock_at: locked_lp_unlock_at_v,
        locked_lp_recipient: locked_lp_recipient_v,
        augmented_fee,
    };

    // Save pool
    POOLS.save(deps.storage, &token_addr, &pool)?;

    let mut response = Response::new()
        .add_attribute("action", "create_pool")
        .add_attribute("token_address", token_address)
        .add_attribute("bwick_reserve", bwick_amount)
        .add_attribute("token_reserve", token_amount)
        .add_attribute("lp_total_supply", lp_total_supply.to_string());

    // Add augmented fee attributes if configured
    if let Some(ref aug_fee) = pool.augmented_fee {
        response = response
            .add_attribute("augmented_fee_bps", aug_fee.augmented_fee_bps.to_string())
            .add_attribute("lp_target_ubwick", aug_fee.lp_target_ubwick.to_string());
    }

    // v3: surface lock metadata so indexers don't have to query the pool back.
    if let (Some(t), Some(r)) = (pool.locked_lp_unlock_at, pool.locked_lp_recipient.as_ref()) {
        response = response
            .add_attribute("locked_lp_unlock_at", t.to_string())
            .add_attribute("locked_lp_recipient", r.to_string());
    } else {
        response = response.add_attribute("locked_lp_unlock_at", "forever");
    }

    Ok(response)
}

/// Check that buyer's holding after purchase won't exceed max_wallet_bps of token's total supply.
/// Queries the CW20 contract for total_supply and the buyer's current balance.
/// Skips the check if max_wallet_bps is 0 (disabled).
fn check_max_wallet_amm(
    deps: &DepsMut,
    token_address: &str,
    buyer_address: &str,
    tokens_out: u128,
    max_wallet_bps: u16,
) -> Result<(), ContractError> {
    if max_wallet_bps == 0 {
        return Ok(());
    }

    // Query CW20 total supply
    let token_info: TokenInfoResponse = deps.querier.query_wasm_smart(
        token_address,
        &cw20::Cw20QueryMsg::TokenInfo {},
    )?;
    let total_supply = token_info.total_supply.u128();
    let limit = total_supply * (max_wallet_bps as u128) / 10000;

    // Query buyer's current CW20 balance
    let balance_resp: cw20::BalanceResponse = deps.querier.query_wasm_smart(
        token_address,
        &cw20::Cw20QueryMsg::Balance {
            address: buyer_address.to_string(),
        },
    )?;
    let current = balance_resp.balance.u128();
    let would_become = current + tokens_out;

    if would_become > limit {
        return Err(ContractError::MaxWalletExceeded {
            current,
            buying: tokens_out,
            would_become,
            limit,
        });
    }

    Ok(())
}

// ── v2: open LP add/remove ────────────────────────────────────────────────────

fn execute_add_liquidity(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    token_address: String,
    max_token_amount: Uint128,
) -> Result<Response, ContractError> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let mut pool = POOLS.load(deps.storage, &token_addr).map_err(|_| {
        ContractError::PoolNotFound { token: token_address.clone() }
    })?;

    if pool.lp_total_supply == 0 || pool.bwick_reserve == 0 || pool.token_reserve == 0 {
        return Err(ContractError::Std(StdError::generic_err(
            "pool not initialized; cannot add liquidity",
        )));
    }

    // BWICK side: from attached funds (convert to u128 for arithmetic).
    let bwick_added: u128 = extract_bwick_from_funds(&info.funds)?.u128();
    if bwick_added == 0 {
        return Err(ContractError::ZeroAmount {});
    }

    // Required token amount to maintain pool ratio (round up, conservative for the pool).
    // token_required = ceil(bwick_added * token_reserve / bwick_reserve)
    let token_required_num = bwick_added
        .checked_mul(pool.token_reserve)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("overflow in token requirement")))?;
    let token_required = (token_required_num + pool.bwick_reserve - 1) / pool.bwick_reserve;
    if token_required > max_token_amount.u128() {
        return Err(ContractError::Std(StdError::generic_err(format!(
            "slippage: token_required={} exceeds max_token_amount={}",
            token_required, max_token_amount.u128()
        ))));
    }

    // LP shares minted: proportional to BWICK side (round down so existing LPs aren't diluted by rounding).
    let shares_minted = pool.lp_total_supply
        .checked_mul(bwick_added)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("overflow in lp shares")))?
        / pool.bwick_reserve;
    if shares_minted == 0 {
        return Err(ContractError::Std(StdError::generic_err("deposit too small to mint shares")));
    }

    // Pull tokens from caller via TransferFrom (caller must have set allowance).
    let pull_tokens = WasmMsg::Execute {
        contract_addr: token_address.clone(),
        msg: to_binary(&cw20::Cw20ExecuteMsg::TransferFrom {
            owner: info.sender.to_string(),
            recipient: _env.contract.address.to_string(),
            amount: Uint128::from(token_required),
        })?,
        funds: vec![],
    };

    // Update reserves and supply.
    pool.bwick_reserve = pool.bwick_reserve.checked_add(bwick_added)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("bwick_reserve overflow")))?;
    pool.token_reserve = pool.token_reserve.checked_add(token_required)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("token_reserve overflow")))?;
    pool.lp_total_supply = pool.lp_total_supply.checked_add(shares_minted)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("lp_total_supply overflow")))?;
    POOLS.save(deps.storage, &token_addr, &pool)?;

    // Credit shares to caller's open LP balance.
    let prev = OPEN_LP.may_load(deps.storage, (&token_addr, &info.sender))?.unwrap_or(0);
    OPEN_LP.save(deps.storage, (&token_addr, &info.sender), &(prev + shares_minted))?;

    Ok(Response::new()
        .add_message(pull_tokens)
        .add_attribute("action", "add_liquidity")
        .add_attribute("token_address", token_address)
        .add_attribute("provider", info.sender)
        .add_attribute("bwick_added", bwick_added.to_string())
        .add_attribute("token_added", token_required.to_string())
        .add_attribute("lp_shares_minted", shares_minted.to_string())
        .add_attribute("new_lp_total_supply", pool.lp_total_supply.to_string()))
}

fn execute_remove_liquidity(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    token_address: String,
    lp_amount: Uint128,
) -> Result<Response, ContractError> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let mut pool = POOLS.load(deps.storage, &token_addr).map_err(|_| {
        ContractError::PoolNotFound { token: token_address.clone() }
    })?;

    let lp = lp_amount.u128();
    if lp == 0 {
        return Err(ContractError::ZeroAmount {});
    }

    // Caller must own at least `lp` open LP shares.
    let holder_shares = OPEN_LP
        .may_load(deps.storage, (&token_addr, &info.sender))?
        .unwrap_or(0);
    if lp > holder_shares {
        return Err(ContractError::Std(StdError::generic_err(format!(
            "insufficient lp balance: have {}, need {}",
            holder_shares, lp
        ))));
    }

    // Compute proportional withdrawal (round down so the pool keeps the dust).
    if pool.lp_total_supply == 0 {
        return Err(ContractError::Std(StdError::generic_err("pool empty")));
    }
    let bwick_out = pool.bwick_reserve
        .checked_mul(lp)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("overflow in bwick_out")))?
        / pool.lp_total_supply;
    let token_out = pool.token_reserve
        .checked_mul(lp)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("overflow in token_out")))?
        / pool.lp_total_supply;
    if bwick_out == 0 || token_out == 0 {
        return Err(ContractError::Std(StdError::generic_err(
            "lp_amount too small to withdraw any reserves",
        )));
    }

    // Update reserves and supply.
    pool.bwick_reserve = pool.bwick_reserve.checked_sub(bwick_out)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("bwick_reserve underflow")))?;
    pool.token_reserve = pool.token_reserve.checked_sub(token_out)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("token_reserve underflow")))?;
    pool.lp_total_supply = pool.lp_total_supply.checked_sub(lp)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("lp_total_supply underflow")))?;
    POOLS.save(deps.storage, &token_addr, &pool)?;

    // Decrement caller's open LP balance.
    let new_balance = holder_shares - lp;
    if new_balance == 0 {
        OPEN_LP.remove(deps.storage, (&token_addr, &info.sender));
    } else {
        OPEN_LP.save(deps.storage, (&token_addr, &info.sender), &new_balance)?;
    }

    // Send BWICK + tokens back to caller.
    let send_bwick = BankMsg::Send {
        to_address: info.sender.to_string(),
        amount: vec![Coin { denom: "ubwick".to_string(), amount: Uint128::from(bwick_out) }],
    };
    let send_tokens = WasmMsg::Execute {
        contract_addr: token_address.clone(),
        msg: to_binary(&cw20::Cw20ExecuteMsg::Transfer {
            recipient: info.sender.to_string(),
            amount: Uint128::from(token_out),
        })?,
        funds: vec![],
    };

    Ok(Response::new()
        .add_message(send_bwick)
        .add_message(send_tokens)
        .add_attribute("action", "remove_liquidity")
        .add_attribute("token_address", token_address)
        .add_attribute("provider", info.sender)
        .add_attribute("lp_burned", lp.to_string())
        .add_attribute("bwick_returned", bwick_out.to_string())
        .add_attribute("tokens_returned", token_out.to_string())
        .add_attribute("new_lp_total_supply", pool.lp_total_supply.to_string()))
}

// v3: withdraw the locked LP seed once the configured timestamp has passed.
// This is the only path that can decrement `locked_lp_supply`. Open LP
// holders keep their proportional claim because we decrement
// `lp_total_supply` by the same amount we burn from `locked_lp_supply`.
fn execute_withdraw_locked_lp(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    token_address: String,
    recipient: Option<String>,
) -> Result<Response, ContractError> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let mut pool = POOLS.load(deps.storage, &token_addr).map_err(|_| {
        ContractError::PoolNotFound { token: token_address.clone() }
    })?;

    let unlock_at = pool.locked_lp_unlock_at.ok_or(ContractError::LockedLpPermanent {})?;
    let configured_recipient = pool
        .locked_lp_recipient
        .clone()
        .ok_or(ContractError::LockedLpPermanent {})?;

    let now = env.block.time.seconds();
    if now < unlock_at {
        return Err(ContractError::LockedLpStillLocked { unlock_at, now });
    }
    if info.sender != configured_recipient {
        return Err(ContractError::LockedLpUnauthorized {});
    }
    if pool.locked_lp_supply == 0 {
        return Err(ContractError::LockedLpAlreadyWithdrawn {});
    }
    if pool.lp_total_supply == 0 {
        return Err(ContractError::Std(StdError::generic_err("pool empty")));
    }

    let lp = pool.locked_lp_supply;
    let bwick_out = pool
        .bwick_reserve
        .checked_mul(lp)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("overflow in bwick_out")))?
        / pool.lp_total_supply;
    let token_out = pool
        .token_reserve
        .checked_mul(lp)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("overflow in token_out")))?
        / pool.lp_total_supply;

    pool.bwick_reserve = pool
        .bwick_reserve
        .checked_sub(bwick_out)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("bwick_reserve underflow")))?;
    pool.token_reserve = pool
        .token_reserve
        .checked_sub(token_out)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("token_reserve underflow")))?;
    pool.lp_total_supply = pool
        .lp_total_supply
        .checked_sub(lp)
        .ok_or_else(|| ContractError::Std(StdError::generic_err("lp_total_supply underflow")))?;
    pool.locked_lp_supply = 0;
    POOLS.save(deps.storage, &token_addr, &pool)?;

    let to_addr = match recipient {
        Some(r) => deps.api.addr_validate(&r)?,
        None => configured_recipient,
    };

    let send_bwick = BankMsg::Send {
        to_address: to_addr.to_string(),
        amount: vec![Coin { denom: "ubwick".to_string(), amount: Uint128::from(bwick_out) }],
    };
    let send_tokens = WasmMsg::Execute {
        contract_addr: token_address.clone(),
        msg: to_binary(&cw20::Cw20ExecuteMsg::Transfer {
            recipient: to_addr.to_string(),
            amount: Uint128::from(token_out),
        })?,
        funds: vec![],
    };

    Ok(Response::new()
        .add_message(send_bwick)
        .add_message(send_tokens)
        .add_attribute("action", "withdraw_locked_lp")
        .add_attribute("token_address", token_address)
        .add_attribute("recipient", to_addr)
        .add_attribute("lp_burned", lp.to_string())
        .add_attribute("bwick_returned", bwick_out.to_string())
        .add_attribute("tokens_returned", token_out.to_string())
        .add_attribute("new_lp_total_supply", pool.lp_total_supply.to_string()))
}

fn execute_swap(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    token_address: String,
    offer_bwick: bool,
    min_output: Uint128,
) -> Result<Response, ContractError> {
    let config = CONFIG.load(deps.storage)?;
    let token_addr = deps.api.addr_validate(&token_address)?;

    if !offer_bwick {
        return Err(ContractError::Std(StdError::generic_err(
            "For Token->BWICK swaps, use CW20 Send to this contract with SwapTokenForBwick message",
        )));
    }

    // BWICK -> Token swap
    let mut pool = POOLS.load(deps.storage, &token_addr)?;

    // Extract BWICK input amount
    let input_amount = extract_bwick_from_funds(&info.funds)?;
    if input_amount.is_zero() {
        return Err(ContractError::ZeroAmount {});
    }

    // Calculate total fee (base + augmented if active)
    let base_fee_bps = config.swap_fee_bps;
    let total_fee_bps = if let Some(ref aug_fee) = pool.augmented_fee {
        if aug_fee.active {
            base_fee_bps + aug_fee.augmented_fee_bps
        } else {
            base_fee_bps
        }
    } else {
        base_fee_bps
    };

    // Calculate output using constant product
    let (output_amount, fee_amount) = calculate_swap_output(
        pool.bwick_reserve,
        pool.token_reserve,
        input_amount.u128(),
        total_fee_bps,
    )?;

    // Check slippage protection
    if Uint128::new(output_amount) < min_output {
        return Err(ContractError::SlippageExceeded {
            expected: min_output.u128(),
            actual: output_amount,
        });
    }

    // Check max wallet holding limit (BWICK->Token direction only = buying tokens)
    check_max_wallet_amm(
        &deps,
        &token_address,
        info.sender.as_str(),
        output_amount,
        config.max_wallet_bps,
    )?;

    // Update pool reserves
    // Fee stays in pool (auto-compound) - input includes fee
    pool.bwick_reserve += input_amount.u128();
    pool.token_reserve -= output_amount;

    // Check if augmented fee should auto-disable
    let mut response = Response::new();
    if let Some(ref mut aug_fee) = pool.augmented_fee {
        if aug_fee.active {
            let pool_value = pool.bwick_reserve * 2;
            if pool_value >= aug_fee.lp_target_ubwick {
                aug_fee.active = false;
                response = response
                    .add_attribute("augmented_fee_disabled", "true")
                    .add_attribute("pool_value", pool_value.to_string())
                    .add_attribute("lp_target", aug_fee.lp_target_ubwick.to_string());
            }
        }
    }

    // Save updated pool
    POOLS.save(deps.storage, &token_addr, &pool)?;

    // Transfer CW20 tokens to sender
    let transfer_msg = WasmMsg::Execute {
        contract_addr: token_address.clone(),
        msg: to_binary(&cw20::Cw20ExecuteMsg::Transfer {
            recipient: info.sender.to_string(),
            amount: Uint128::new(output_amount),
        })?,
        funds: vec![],
    };

    // Calculate augmented fee portion for attribute
    let augmented_fee_amount = if total_fee_bps > base_fee_bps {
        (input_amount.u128() * (total_fee_bps - base_fee_bps) as u128) / 10000
    } else {
        0
    };

    Ok(response
        .add_message(transfer_msg)
        .add_attribute("action", "swap")
        .add_attribute("direction", "bwick_to_token")
        .add_attribute("token_address", token_address)
        .add_attribute("input_amount", input_amount)
        .add_attribute("output_amount", output_amount.to_string())
        .add_attribute("fee_amount", fee_amount.to_string())
        .add_attribute("augmented_fee_amount", augmented_fee_amount.to_string()))
}

fn execute_receive(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    cw20_msg: Cw20ReceiveMsg,
) -> Result<Response, ContractError> {
    let config = CONFIG.load(deps.storage)?;

    // info.sender is the CW20 contract address
    let token_addr = info.sender;

    // cw20_msg.sender is the original sender (user who initiated CW20 Send)
    let user_addr = deps.api.addr_validate(&cw20_msg.sender)?;

    // Decode the message
    let swap_msg: SwapTokenForBwick = cosmwasm_std::from_slice(&cw20_msg.msg)?;

    // Token -> BWICK swap
    let mut pool = POOLS.load(deps.storage, &token_addr)?;

    let input_amount = cw20_msg.amount.u128();
    if input_amount == 0 {
        return Err(ContractError::ZeroAmount {});
    }

    // Calculate total fee (base + augmented if active)
    let base_fee_bps = config.swap_fee_bps;
    let total_fee_bps = if let Some(ref aug_fee) = pool.augmented_fee {
        if aug_fee.active {
            base_fee_bps + aug_fee.augmented_fee_bps
        } else {
            base_fee_bps
        }
    } else {
        base_fee_bps
    };

    // Calculate output using constant product
    let (output_amount, fee_amount) = calculate_swap_output(
        pool.token_reserve,
        pool.bwick_reserve,
        input_amount,
        total_fee_bps,
    )?;

    // Check slippage protection
    if Uint128::new(output_amount) < swap_msg.min_output {
        return Err(ContractError::SlippageExceeded {
            expected: swap_msg.min_output.u128(),
            actual: output_amount,
        });
    }

    // Update pool reserves
    // Fee stays in pool (auto-compound) - input includes fee
    pool.token_reserve += input_amount;
    pool.bwick_reserve -= output_amount;

    // Check if augmented fee should auto-disable
    let mut response = Response::new();
    if let Some(ref mut aug_fee) = pool.augmented_fee {
        if aug_fee.active {
            let pool_value = pool.bwick_reserve * 2;
            if pool_value >= aug_fee.lp_target_ubwick {
                aug_fee.active = false;
                response = response
                    .add_attribute("augmented_fee_disabled", "true")
                    .add_attribute("pool_value", pool_value.to_string())
                    .add_attribute("lp_target", aug_fee.lp_target_ubwick.to_string());
            }
        }
    }

    // Save updated pool
    POOLS.save(deps.storage, &token_addr, &pool)?;

    // Transfer native BWICK to the original sender
    let send_msg = BankMsg::Send {
        to_address: user_addr.to_string(),
        amount: vec![Coin {
            denom: "ubwick".to_string(),
            amount: Uint128::new(output_amount),
        }],
    };

    // Calculate augmented fee portion for attribute
    let augmented_fee_amount = if total_fee_bps > base_fee_bps {
        (input_amount * (total_fee_bps - base_fee_bps) as u128) / 10000
    } else {
        0
    };

    Ok(response
        .add_message(send_msg)
        .add_attribute("action", "swap")
        .add_attribute("direction", "token_to_bwick")
        .add_attribute("token_address", token_addr.to_string())
        .add_attribute("input_amount", input_amount.to_string())
        .add_attribute("output_amount", output_amount.to_string())
        .add_attribute("fee_amount", fee_amount.to_string())
        .add_attribute("augmented_fee_amount", augmented_fee_amount.to_string())
        .add_attribute("recipient", user_addr.to_string()))
}

// Helper functions

/// Calculate output amount using constant product formula
/// Returns (output_amount, fee_amount)
fn calculate_swap_output(
    input_reserve: u128,
    output_reserve: u128,
    input_amount: u128,
    fee_bps: u16,
) -> Result<(u128, u128), ContractError> {
    // Deduct fee from input first
    let fee_amount = input_amount * (fee_bps as u128) / 10000;
    let input_after_fee = input_amount - fee_amount;

    // Constant product: x * y = k
    // new_input_reserve * new_output_reserve = k
    // (input_reserve + input_after_fee) * (output_reserve - output_amount) = input_reserve * output_reserve
    // output_amount = output_reserve - (input_reserve * output_reserve) / (input_reserve + input_after_fee)
    // output_amount = output_reserve * input_after_fee / (input_reserve + input_after_fee)

    let numerator = output_reserve * input_after_fee;
    let denominator = input_reserve + input_after_fee;

    if denominator == 0 {
        return Err(ContractError::InsufficientLiquidity {});
    }

    let output_amount = numerator / denominator;

    if output_amount == 0 {
        return Err(ContractError::InsufficientLiquidity {});
    }

    if output_amount >= output_reserve {
        return Err(ContractError::InsufficientLiquidity {});
    }

    Ok((output_amount, fee_amount))
}

/// Calculate integer square root using Newton's method
fn integer_sqrt(n: u128) -> u128 {
    if n == 0 {
        return 0;
    }

    let mut x = n;
    let mut y = x.div_ceil(2);

    while y < x {
        x = y;
        y = (x + n / x).div_ceil(2);
    }

    x
}

/// Validate that the token is a valid CW20 contract
fn validate_cw20_token(deps: Deps, token_addr: &Addr) -> Result<(), ContractError> {
    // Query token_info to verify it's a valid CW20
    let query_msg = cw20::Cw20QueryMsg::TokenInfo {};
    let _: TokenInfoResponse = deps
        .querier
        .query_wasm_smart(token_addr.to_string(), &query_msg)
        .map_err(|_| {
            ContractError::Std(StdError::generic_err(format!(
                "Invalid CW20 token address: {}",
                token_addr
            )))
        })?;
    Ok(())
}

/// Extract BWICK amount from MessageInfo funds
fn extract_bwick_from_funds(funds: &[Coin]) -> Result<Uint128, ContractError> {
    // Must have exactly one coin with denom "ubwick"
    if funds.len() != 1 {
        return Err(ContractError::InvalidToken {});
    }

    let coin = &funds[0];
    if coin.denom != "ubwick" {
        return Err(ContractError::InvalidToken {});
    }

    Ok(coin.amount)
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<Binary> {
    match msg {
        QueryMsg::Pool { token_address } => to_binary(&query_pool(deps, token_address)?),
        QueryMsg::AllPools { start_after, limit } => {
            to_binary(&query_all_pools(deps, start_after, limit)?)
        }
        QueryMsg::SimulateSwap { token_address, offer_bwick, offer_amount } => {
            to_binary(&query_simulate_swap(deps, token_address, offer_bwick, offer_amount)?)
        }
        QueryMsg::Config {} => to_binary(&query_config(deps)?),
        QueryMsg::AugmentedFeeStatus { token_address } => {
            to_binary(&query_augmented_fee_status(deps, token_address)?)
        }
        QueryMsg::LpBalance { token_address, holder } => {
            to_binary(&query_lp_balance(deps, token_address, holder)?)
        }
        QueryMsg::SimulateAddLiquidity { token_address, bwick_amount } => {
            to_binary(&query_simulate_add_liquidity(deps, token_address, bwick_amount)?)
        }
        QueryMsg::LockedLp { token_address } => {
            to_binary(&query_locked_lp(deps, env, token_address)?)
        }
    }
}

fn query_locked_lp(
    deps: Deps,
    env: Env,
    token_address: String,
) -> StdResult<crate::msg::LockedLpResponse> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let pool = POOLS
        .load(deps.storage, &token_addr)
        .map_err(|_| StdError::not_found(format!("Pool for token {}", token_address)))?;
    let now = env.block.time.seconds();
    let (bwick_share, token_share) = if pool.lp_total_supply > 0 && pool.locked_lp_supply > 0 {
        (
            pool.bwick_reserve.saturating_mul(pool.locked_lp_supply) / pool.lp_total_supply,
            pool.token_reserve.saturating_mul(pool.locked_lp_supply) / pool.lp_total_supply,
        )
    } else {
        (0u128, 0u128)
    };
    let unlocked = matches!(pool.locked_lp_unlock_at, Some(t) if now >= t);
    Ok(crate::msg::LockedLpResponse {
        token_address: token_addr,
        locked_lp_supply: Uint128::from(pool.locked_lp_supply),
        unlock_at: pool.locked_lp_unlock_at,
        recipient: pool.locked_lp_recipient,
        unlocked,
        bwick_share: Uint128::from(bwick_share),
        token_share: Uint128::from(token_share),
    })
}

fn query_lp_balance(
    deps: Deps,
    token_address: String,
    holder: String,
) -> StdResult<LpBalanceResponse> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let holder_addr = deps.api.addr_validate(&holder)?;
    let pool = POOLS.load(deps.storage, &token_addr)
        .map_err(|_| StdError::not_found(format!("Pool for token {}", token_address)))?;
    let shares = OPEN_LP.may_load(deps.storage, (&token_addr, &holder_addr))?.unwrap_or(0);

    let (bwick_share, token_share) = if pool.lp_total_supply > 0 && shares > 0 {
        (
            pool.bwick_reserve.saturating_mul(shares) / pool.lp_total_supply,
            pool.token_reserve.saturating_mul(shares) / pool.lp_total_supply,
        )
    } else {
        (0u128, 0u128)
    };

    Ok(LpBalanceResponse {
        holder: holder_addr,
        token_address: token_addr,
        shares: Uint128::from(shares),
        bwick_share: Uint128::from(bwick_share),
        token_share: Uint128::from(token_share),
        lp_total_supply: Uint128::from(pool.lp_total_supply),
        locked_lp_supply: Uint128::from(pool.locked_lp_supply),
    })
}

fn query_simulate_add_liquidity(
    deps: Deps,
    token_address: String,
    bwick_amount: Uint128,
) -> StdResult<SimulateAddLiquidityResponse> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let pool = POOLS.load(deps.storage, &token_addr)
        .map_err(|_| StdError::not_found(format!("Pool for token {}", token_address)))?;
    if pool.lp_total_supply == 0 || pool.bwick_reserve == 0 {
        return Err(StdError::generic_err("pool not initialized"));
    }
    let bwick_added = bwick_amount.u128();
    if bwick_added == 0 {
        return Err(StdError::generic_err("bwick_amount must be positive"));
    }
    let token_required_num = bwick_added.saturating_mul(pool.token_reserve);
    let token_required = (token_required_num + pool.bwick_reserve - 1) / pool.bwick_reserve;
    let lp_shares = pool.lp_total_supply.saturating_mul(bwick_added) / pool.bwick_reserve;
    Ok(SimulateAddLiquidityResponse {
        token_amount_required: Uint128::from(token_required),
        lp_shares_minted: Uint128::from(lp_shares),
        bwick_reserve_before: Uint128::from(pool.bwick_reserve),
        token_reserve_before: Uint128::from(pool.token_reserve),
    })
}

fn query_pool(deps: Deps, token_address: String) -> StdResult<PoolResponse> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let pool = POOLS.load(deps.storage, &token_addr)
        .map_err(|_| StdError::not_found(format!("Pool for token {}", token_address)))?;

    // Calculate price: BWICK per token
    let price = if pool.token_reserve > 0 {
        format!("{:.6}", pool.bwick_reserve as f64 / pool.token_reserve as f64)
    } else {
        "0".to_string()
    };

    Ok(PoolResponse {
        token_address: pool.token_address,
        bwick_reserve: Uint128::from(pool.bwick_reserve),
        token_reserve: Uint128::from(pool.token_reserve),
        lp_token_address: pool.lp_token_address,
        lp_total_supply: Uint128::from(pool.lp_total_supply),
        price,
        locked_lp_unlock_at: pool.locked_lp_unlock_at,
    })
}

fn query_all_pools(
    deps: Deps,
    start_after: Option<String>,
    limit: Option<u32>,
) -> StdResult<AllPoolsResponse> {
    let limit = limit.unwrap_or(10).min(30) as usize;
    let start = start_after
        .map(|s| deps.api.addr_validate(&s))
        .transpose()?;

    let pools: Vec<PoolResponse> = POOLS
        .range(
            deps.storage,
            start.as_ref().map(Bound::exclusive),
            None,
            Order::Ascending,
        )
        .take(limit)
        .map(|item| {
            let (_, pool) = item?;
            let price = if pool.token_reserve > 0 {
                format!("{:.6}", pool.bwick_reserve as f64 / pool.token_reserve as f64)
            } else {
                "0".to_string()
            };
            Ok(PoolResponse {
                token_address: pool.token_address,
                bwick_reserve: Uint128::from(pool.bwick_reserve),
                token_reserve: Uint128::from(pool.token_reserve),
                lp_token_address: pool.lp_token_address,
                lp_total_supply: Uint128::from(pool.lp_total_supply),
                price,
                locked_lp_unlock_at: pool.locked_lp_unlock_at,
            })
        })
        .collect::<StdResult<Vec<_>>>()?;

    Ok(AllPoolsResponse { pools })
}

fn query_simulate_swap(
    deps: Deps,
    token_address: String,
    offer_bwick: bool,
    offer_amount: Uint128,
) -> StdResult<SimulateSwapResponse> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let pool = POOLS.load(deps.storage, &token_addr)
        .map_err(|_| StdError::not_found(format!("Pool for token {}", token_address)))?;
    let config = CONFIG.load(deps.storage)?;

    let (input_reserve, output_reserve) = if offer_bwick {
        (pool.bwick_reserve, pool.token_reserve)
    } else {
        (pool.token_reserve, pool.bwick_reserve)
    };

    // Calculate total fee (base + augmented if active)
    let base_fee_bps = config.swap_fee_bps;
    let total_fee_bps = if let Some(ref aug_fee) = pool.augmented_fee {
        if aug_fee.active {
            base_fee_bps + aug_fee.augmented_fee_bps
        } else {
            base_fee_bps
        }
    } else {
        base_fee_bps
    };

    let offer = offer_amount.u128();
    let (output_amount, fee_amount) = calculate_swap_output(
        input_reserve,
        output_reserve,
        offer,
        total_fee_bps,
    ).map_err(|e| StdError::generic_err(format!("Swap calculation failed: {:?}", e)))?;

    // Calculate price impact
    // Price impact = (new_price - old_price) / old_price
    // old_price = output_reserve / input_reserve
    // new_price = (output_reserve - output) / (input_reserve + offer - fee)
    let old_price = output_reserve as f64 / input_reserve as f64;
    let new_input = input_reserve + offer - fee_amount;
    let new_output = output_reserve - output_amount;
    let new_price = new_output as f64 / new_input as f64;
    let price_impact = ((old_price - new_price) / old_price * 100.0).abs();

    // Calculate augmented fee portion
    let augmented_fee_amount = if total_fee_bps > base_fee_bps {
        (offer * (total_fee_bps - base_fee_bps) as u128) / 10000
    } else {
        0
    };

    Ok(SimulateSwapResponse {
        output_amount: Uint128::from(output_amount),
        fee_amount: Uint128::from(fee_amount),
        price_impact: format!("{:.2}%", price_impact),
        augmented_fee_amount: Uint128::from(augmented_fee_amount),
    })
}

fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
    let config = CONFIG.load(deps.storage)?;
    Ok(ConfigResponse {
        authorized_creators: config.authorized_creators,
        swap_fee_bps: config.swap_fee_bps,
        max_wallet_bps: config.max_wallet_bps,
    })
}

fn query_augmented_fee_status(
    deps: Deps,
    token_address: String,
) -> StdResult<AugmentedFeeStatusResponse> {
    let token_addr = deps.api.addr_validate(&token_address)?;
    let pool = POOLS.load(deps.storage, &token_addr)
        .map_err(|_| StdError::not_found(format!("Pool for token {}", token_address)))?;

    let current_pool_value = pool.bwick_reserve * 2;

    match pool.augmented_fee {
        Some(aug_fee) => {
            let progress_percent = if aug_fee.lp_target_ubwick > 0 {
                format!(
                    "{:.2}",
                    (current_pool_value as f64 / aug_fee.lp_target_ubwick as f64) * 100.0
                )
            } else {
                "0.00".to_string()
            };

            Ok(AugmentedFeeStatusResponse {
                active: aug_fee.active,
                augmented_fee_bps: aug_fee.augmented_fee_bps,
                lp_target_ubwick: Uint128::from(aug_fee.lp_target_ubwick),
                current_pool_value_ubwick: Uint128::from(current_pool_value),
                progress_percent,
            })
        }
        None => Ok(AugmentedFeeStatusResponse {
            active: false,
            augmented_fee_bps: 0,
            lp_target_ubwick: Uint128::zero(),
            current_pool_value_ubwick: Uint128::from(current_pool_value),
            progress_percent: "N/A".to_string(),
        }),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use cosmwasm_std::testing::mock_dependencies;
    use cosmwasm_std::Addr;

    const BONDING_CURVE: &str = "bonding_curve_contract";
    const TOKEN: &str = "token_contract";

    /// Setup contract state directly without going through instantiate
    /// This avoids bech32 address validation in mock_dependencies
    fn setup_contract_state(deps: DepsMut) {
        let config = Config {
            authorized_creators: vec![Addr::unchecked(BONDING_CURVE)],
            swap_fee_bps: 100, // 1% fee
            max_wallet_bps: 300, // 3% max wallet
        };
        CONFIG.save(deps.storage, &config).unwrap();
    }

    fn setup_pool(deps: DepsMut, bwick_reserve: u128, token_reserve: u128) {
        let token_addr = Addr::unchecked(TOKEN);
        let pool = Pool {
            token_address: token_addr.clone(),
            bwick_reserve,
            token_reserve,
            lp_token_address: Addr::unchecked("contract"),
            lp_total_supply: integer_sqrt(bwick_reserve * token_reserve),
            locked_lp_supply: 0,
            locked_lp_unlock_at: None,
            locked_lp_recipient: None,
            augmented_fee: None,
        };
        POOLS.save(deps.storage, &token_addr, &pool).unwrap();
    }

    // ==================== v3: LP lock tests ====================

    fn setup_locked_pool(
        deps: DepsMut,
        bwick_reserve: u128,
        token_reserve: u128,
        locked_supply: u128,
        unlock_at: Option<u64>,
        recipient: Option<&str>,
    ) {
        let token_addr = Addr::unchecked(TOKEN);
        let pool = Pool {
            token_address: token_addr.clone(),
            bwick_reserve,
            token_reserve,
            lp_token_address: Addr::unchecked("contract"),
            lp_total_supply: locked_supply,
            locked_lp_supply: locked_supply,
            locked_lp_unlock_at: unlock_at,
            locked_lp_recipient: recipient.map(Addr::unchecked),
            augmented_fee: None,
        };
        POOLS.save(deps.storage, &token_addr, &pool).unwrap();
    }

    #[test]
    fn withdraw_locked_lp_rejects_permanent_lock() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());
        setup_locked_pool(deps.as_mut(), 1000, 1000, 1000, None, None);
        let info = cosmwasm_std::testing::mock_info("anyone", &[]);
        let env = cosmwasm_std::testing::mock_env();
        let err = execute_withdraw_locked_lp(
            deps.as_mut(),
            env,
            info,
            TOKEN.to_string(),
            None,
        )
        .unwrap_err();
        assert!(matches!(err, ContractError::LockedLpPermanent {}));
    }

    #[test]
    fn withdraw_locked_lp_rejects_before_unlock() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());
        let mut env = cosmwasm_std::testing::mock_env();
        let unlock_at = env.block.time.seconds() + 10_000;
        setup_locked_pool(
            deps.as_mut(),
            1000,
            1000,
            1000,
            Some(unlock_at),
            Some("creator"),
        );
        let info = cosmwasm_std::testing::mock_info("creator", &[]);
        env.block.time = cosmwasm_std::Timestamp::from_seconds(unlock_at - 1);
        let err = execute_withdraw_locked_lp(
            deps.as_mut(),
            env,
            info,
            TOKEN.to_string(),
            None,
        )
        .unwrap_err();
        assert!(matches!(err, ContractError::LockedLpStillLocked { .. }));
    }

    #[test]
    fn withdraw_locked_lp_rejects_wrong_caller() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());
        let mut env = cosmwasm_std::testing::mock_env();
        let unlock_at = env.block.time.seconds();
        setup_locked_pool(
            deps.as_mut(),
            1000,
            1000,
            1000,
            Some(unlock_at),
            Some("creator"),
        );
        let info = cosmwasm_std::testing::mock_info("imposter", &[]);
        env.block.time = cosmwasm_std::Timestamp::from_seconds(unlock_at + 1);
        let err = execute_withdraw_locked_lp(
            deps.as_mut(),
            env,
            info,
            TOKEN.to_string(),
            None,
        )
        .unwrap_err();
        assert!(matches!(err, ContractError::LockedLpUnauthorized {}));
    }

    #[test]
    fn withdraw_locked_lp_succeeds_after_unlock_and_drains_seed() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());
        let mut env = cosmwasm_std::testing::mock_env();
        let unlock_at = env.block.time.seconds();
        // pool: 1000 BWICK, 5000 tokens, 1000 LP, all locked.
        setup_locked_pool(
            deps.as_mut(),
            1000,
            5000,
            1000,
            Some(unlock_at),
            Some("creator"),
        );
        env.block.time = cosmwasm_std::Timestamp::from_seconds(unlock_at);
        let info = cosmwasm_std::testing::mock_info("creator", &[]);
        let res = execute_withdraw_locked_lp(
            deps.as_mut(),
            env,
            info,
            TOKEN.to_string(),
            None,
        )
        .unwrap();
        // 2 messages: bank send BWICK, wasm transfer tokens.
        assert_eq!(res.messages.len(), 2);
        let pool = POOLS
            .load(deps.as_ref().storage, &Addr::unchecked(TOKEN))
            .unwrap();
        assert_eq!(pool.locked_lp_supply, 0);
        assert_eq!(pool.lp_total_supply, 0);
        assert_eq!(pool.bwick_reserve, 0);
        assert_eq!(pool.token_reserve, 0);

        // A second withdraw attempt rejects with AlreadyWithdrawn (since
        // locked_lp_supply is 0). Note: lp_total_supply is also 0 now so
        // either check could trip; we want the dedicated error.
        let info2 = cosmwasm_std::testing::mock_info("creator", &[]);
        let env2 = cosmwasm_std::testing::mock_env();
        let err = execute_withdraw_locked_lp(
            deps.as_mut(),
            env2,
            info2,
            TOKEN.to_string(),
            None,
        )
        .unwrap_err();
        assert!(matches!(err, ContractError::LockedLpAlreadyWithdrawn {}));
    }

    // ==================== Arithmetic Edge Case Tests ====================

    #[test]
    fn test_calculate_swap_output_normal() {
        // Normal swap: 1000 BWICK in, pool has 10000 BWICK and 10000 tokens
        // Fee: 1000 * 100 / 10000 = 10, input after fee = 990
        // Output = 10000 * 990 / (10000 + 990) = 9900000 / 10990 = 900 (integer division)
        let result = calculate_swap_output(10000, 10000, 1000, 100).unwrap();
        assert_eq!(result.0, 900); // output amount (integer division rounds down)
        assert_eq!(result.1, 10);  // fee amount
    }

    #[test]
    fn test_calculate_swap_output_large_values() {
        // Test with large but safe values (10^18 scale typical for tokens)
        let input_reserve: u128 = 1_000_000_000_000_000_000; // 1e18
        let output_reserve: u128 = 1_000_000_000_000_000_000; // 1e18
        let input_amount: u128 = 1_000_000_000_000_000; // 1e15 (0.1% of pool)

        let result = calculate_swap_output(input_reserve, output_reserve, input_amount, 100);
        assert!(result.is_ok());
        let (output, fee) = result.unwrap();
        assert!(output > 0);
        assert!(output < output_reserve);
        assert_eq!(fee, input_amount / 100); // 1% fee
    }

    #[test]
    fn test_calculate_swap_output_max_fee() {
        // Test with maximum fee (100% = 10000 bps)
        // All input becomes fee, so input_after_fee = 0
        // This should still work (output will be 0, triggering InsufficientLiquidity)
        let result = calculate_swap_output(10000, 10000, 1000, 10000);
        assert!(result.is_err());
        match result {
            Err(ContractError::InsufficientLiquidity {}) => {}
            _ => panic!("Expected InsufficientLiquidity error"),
        }
    }

    #[test]
    fn test_calculate_swap_output_zero_fee() {
        // Test with 0% fee
        // Output = 10000 * 1000 / (10000 + 1000) = 10000000 / 11000 = 909
        let result = calculate_swap_output(10000, 10000, 1000, 0).unwrap();
        assert_eq!(result.0, 909); // output amount
        assert_eq!(result.1, 0);   // fee amount
    }

    #[test]
    fn test_calculate_swap_output_small_input() {
        // Very small input relative to pool
        // Input = 1, fee = 0 (rounds down), output should be minimal but > 0
        let result = calculate_swap_output(1_000_000, 1_000_000, 1, 100);
        // Output = 1_000_000 * 1 / (1_000_000 + 1) = 0 (rounds down)
        assert!(result.is_err()); // InsufficientLiquidity because output = 0
    }

    #[test]
    fn test_fee_calculation_boundaries() {
        // Test fee calculation at various boundaries
        // 1 bps = 0.01% fee
        let (_, fee1) = calculate_swap_output(10000, 10000, 10000, 1).unwrap();
        assert_eq!(fee1, 1); // 10000 * 1 / 10000 = 1

        // 9999 bps = 99.99% fee
        let result = calculate_swap_output(10000, 10000, 10000, 9999);
        // Fee = 10000 * 9999 / 10000 = 9999, input after fee = 1
        // Output = 10000 * 1 / (10000 + 1) = 0 (rounds down)
        assert!(result.is_err()); // InsufficientLiquidity
    }

    // ==================== Zero/Empty Handling Tests ====================

    #[test]
    fn test_calculate_swap_zero_input() {
        // Zero input amount
        let result = calculate_swap_output(10000, 10000, 0, 100);
        assert!(result.is_err());
        match result {
            Err(ContractError::InsufficientLiquidity {}) => {}
            _ => panic!("Expected InsufficientLiquidity error for zero input"),
        }
    }

    #[test]
    fn test_calculate_swap_zero_input_reserve() {
        // Zero input reserve (empty pool side)
        let result = calculate_swap_output(0, 10000, 1000, 100);
        // denominator = 0 + (1000 - 10) = 990, not zero
        // numerator = 10000 * 990 = 9900000
        // output = 9900000 / 990 = 10000 >= output_reserve
        assert!(result.is_err());
        match result {
            Err(ContractError::InsufficientLiquidity {}) => {}
            _ => panic!("Expected InsufficientLiquidity error"),
        }
    }

    #[test]
    fn test_calculate_swap_zero_output_reserve() {
        // Zero output reserve (would drain entire side)
        let result = calculate_swap_output(10000, 0, 1000, 100);
        // output = 0 * 990 / 10990 = 0
        assert!(result.is_err());
        match result {
            Err(ContractError::InsufficientLiquidity {}) => {}
            _ => panic!("Expected InsufficientLiquidity error"),
        }
    }

    #[test]
    fn test_query_nonexistent_pool() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Query pool that doesn't exist - use a valid-looking address
        // Since mock_dependencies doesn't validate bech32, just check the pool doesn't exist
        let token_addr = Addr::unchecked("nonexistent_token");
        let result = POOLS.may_load(deps.as_ref().storage, &token_addr).unwrap();
        assert!(result.is_none());
    }

    #[test]
    fn test_simulate_swap_nonexistent_pool() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Directly test the logic - pool doesn't exist
        let token_addr = Addr::unchecked("nonexistent_token");
        let result = POOLS.may_load(deps.as_ref().storage, &token_addr).unwrap();
        assert!(result.is_none());
    }

    // ==================== Slippage Edge Case Tests ====================

    #[test]
    fn test_slippage_exact_match() {
        // Test when output exactly equals min_receive
        // Calculate expected output first
        let (expected_output, _) = calculate_swap_output(10000, 10000, 1000, 100).unwrap();
        assert_eq!(expected_output, 900); // 9900000 / 10990 = 900 (integer division)

        // min_output exactly equals expected output - should pass
        let min_output = Uint128::new(expected_output);
        let actual = Uint128::new(expected_output);

        // This is the check from execute_swap
        assert!(actual >= min_output); // Should pass (equal)
    }

    #[test]
    fn test_slippage_off_by_one_fail() {
        // Test when output is 1 less than min_receive - should fail
        let (expected_output, _) = calculate_swap_output(10000, 10000, 1000, 100).unwrap();
        assert_eq!(expected_output, 900); // 9900000 / 10990 = 900

        // min_output is 1 more than actual output
        let min_output = Uint128::new(expected_output + 1);
        let actual = Uint128::new(expected_output);

        // This simulates the slippage check
        assert!(actual < min_output); // Would fail slippage check
    }

    #[test]
    fn test_slippage_with_generous_tolerance() {
        // Test with min_output well below actual
        let (expected_output, _) = calculate_swap_output(10000, 10000, 1000, 100).unwrap();

        let min_output = Uint128::new(expected_output / 2); // 50% tolerance
        let actual = Uint128::new(expected_output);

        assert!(actual >= min_output); // Should pass easily
    }

    // ==================== Integer Square Root Tests ====================

    #[test]
    fn test_integer_sqrt_zero() {
        assert_eq!(integer_sqrt(0), 0);
    }

    #[test]
    fn test_integer_sqrt_one() {
        assert_eq!(integer_sqrt(1), 1);
    }

    #[test]
    fn test_integer_sqrt_perfect_squares() {
        assert_eq!(integer_sqrt(4), 2);
        assert_eq!(integer_sqrt(9), 3);
        assert_eq!(integer_sqrt(16), 4);
        assert_eq!(integer_sqrt(100), 10);
        assert_eq!(integer_sqrt(10000), 100);
        assert_eq!(integer_sqrt(1_000_000), 1000);
    }

    #[test]
    fn test_integer_sqrt_non_perfect_squares() {
        // Newton's method with div_ceil - actual behavior verified
        // Note: This implementation uses ceiling division which affects convergence
        assert_eq!(integer_sqrt(2), 1);   // sqrt(2) = 1.41 -> 1
        assert_eq!(integer_sqrt(3), 2);   // sqrt(3) = 1.73 -> 2
        assert_eq!(integer_sqrt(5), 2);   // sqrt(5) = 2.23 -> 2
        assert_eq!(integer_sqrt(8), 3);   // sqrt(8) = 2.83 -> 3
        assert_eq!(integer_sqrt(99), 10); // sqrt(99) = 9.95 -> 10
        assert_eq!(integer_sqrt(101), 10); // sqrt(101) = 10.05 -> 10
    }

    #[test]
    fn test_integer_sqrt_large_value() {
        // Test with large value (10^36 which is sqrt(10^18) * sqrt(10^18))
        let large: u128 = 1_000_000_000_000_000_000_000_000_000_000_000_000; // 10^36
        let sqrt = integer_sqrt(large);
        assert_eq!(sqrt, 1_000_000_000_000_000_000); // 10^18
    }

    // ==================== Instantiation Tests ====================

    #[test]
    fn test_instantiate_stores_config() {
        // Test that config is stored correctly (bypass addr_validate by storing directly)
        let mut deps = mock_dependencies();
        let config = Config {
            authorized_creators: vec![Addr::unchecked(BONDING_CURVE)],
            swap_fee_bps: 100,
            max_wallet_bps: 300,
        };
        CONFIG.save(deps.as_mut().storage, &config).unwrap();

        let loaded = CONFIG.load(deps.as_ref().storage).unwrap();
        assert_eq!(loaded.authorized_creators, vec![Addr::unchecked(BONDING_CURVE)]);
        assert_eq!(loaded.swap_fee_bps, 100);
    }

    #[test]
    fn test_instantiate_fee_validation_logic() {
        // Test the fee validation logic directly
        let valid_fee: u16 = 10000;
        let invalid_fee: u16 = 10001;

        assert!(valid_fee <= 10000);
        assert!(invalid_fee > 10000);
    }

    #[test]
    fn test_instantiate_fee_boundary() {
        // Test fee boundary values
        assert!(100 <= 10000);   // 1% fee is valid
        assert!(0 <= 10000);     // 0% fee is valid
        assert!(10000 <= 10000); // 100% fee is valid (edge case)
        assert!(10001 > 10000);  // 100.01% is invalid
    }

    // ==================== Access Control Tests ====================

    #[test]
    fn test_extract_bwick_from_funds_success() {
        let funds = vec![Coin {
            denom: "ubwick".to_string(),
            amount: Uint128::new(1000),
        }];
        let result = extract_bwick_from_funds(&funds);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), Uint128::new(1000));
    }

    #[test]
    fn test_extract_bwick_from_funds_wrong_denom() {
        let funds = vec![Coin {
            denom: "uatom".to_string(),
            amount: Uint128::new(1000),
        }];
        let result = extract_bwick_from_funds(&funds);
        assert!(result.is_err());
        match result {
            Err(ContractError::InvalidToken {}) => {}
            _ => panic!("Expected InvalidToken error"),
        }
    }

    #[test]
    fn test_extract_bwick_from_funds_multiple_coins() {
        let funds = vec![
            Coin {
                denom: "ubwick".to_string(),
                amount: Uint128::new(1000),
            },
            Coin {
                denom: "uatom".to_string(),
                amount: Uint128::new(500),
            },
        ];
        let result = extract_bwick_from_funds(&funds);
        assert!(result.is_err());
        match result {
            Err(ContractError::InvalidToken {}) => {}
            _ => panic!("Expected InvalidToken error"),
        }
    }

    #[test]
    fn test_extract_bwick_from_funds_empty() {
        let funds: Vec<Coin> = vec![];
        let result = extract_bwick_from_funds(&funds);
        assert!(result.is_err());
        match result {
            Err(ContractError::InvalidToken {}) => {}
            _ => panic!("Expected InvalidToken error"),
        }
    }

    // ==================== Query Tests ====================

    #[test]
    fn test_query_config() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        let result = query_config(deps.as_ref()).unwrap();
        assert_eq!(result.authorized_creators, vec![Addr::unchecked(BONDING_CURVE)]);
        assert_eq!(result.swap_fee_bps, 100);
    }

    #[test]
    fn test_query_pool_direct() {
        // Test pool loading directly without address validation
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());
        setup_pool(deps.as_mut(), 10000, 5000);

        // Load pool directly
        let token_addr = Addr::unchecked(TOKEN);
        let pool = POOLS.load(deps.as_ref().storage, &token_addr).unwrap();
        assert_eq!(pool.bwick_reserve, 10000);
        assert_eq!(pool.token_reserve, 5000);

        // Verify price calculation logic (10000/5000 = 2.0)
        let price = if pool.token_reserve > 0 {
            format!("{:.6}", pool.bwick_reserve as f64 / pool.token_reserve as f64)
        } else {
            "0".to_string()
        };
        assert_eq!(price, "2.000000");
    }

    #[test]
    fn test_query_all_pools_pagination_direct() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Create multiple pools
        for i in 0..5 {
            let token_addr = Addr::unchecked(format!("token{}", i));
            let pool = Pool {
                token_address: token_addr.clone(),
                bwick_reserve: 10000,
                token_reserve: 10000,
                lp_token_address: Addr::unchecked("contract"),
                lp_total_supply: 10000,
                locked_lp_supply: 0,
                locked_lp_unlock_at: None,
                locked_lp_recipient: None,
                augmented_fee: None,
            };
            POOLS.save(deps.as_mut().storage, &token_addr, &pool).unwrap();
        }

        // Query with limit - test range iteration directly
        let pools: Vec<_> = POOLS
            .range(deps.as_ref().storage, None, None, Order::Ascending)
            .take(2)
            .collect::<StdResult<Vec<_>>>()
            .unwrap();
        assert_eq!(pools.len(), 2);

        // Query all
        let pools: Vec<_> = POOLS
            .range(deps.as_ref().storage, None, None, Order::Ascending)
            .take(10)
            .collect::<StdResult<Vec<_>>>()
            .unwrap();
        assert_eq!(pools.len(), 5);
    }

    #[test]
    fn test_query_all_pools_max_limit_direct() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Create 35 pools
        for i in 0..35 {
            let token_addr = Addr::unchecked(format!("token{:02}", i));
            let pool = Pool {
                token_address: token_addr.clone(),
                bwick_reserve: 10000,
                token_reserve: 10000,
                lp_token_address: Addr::unchecked("contract"),
                lp_total_supply: 10000,
                locked_lp_supply: 0,
                locked_lp_unlock_at: None,
                locked_lp_recipient: None,
                augmented_fee: None,
            };
            POOLS.save(deps.as_mut().storage, &token_addr, &pool).unwrap();
        }

        // Request limit > 30 should cap at 30 - test the limit calculation
        let requested_limit: u32 = 50;
        let actual_limit = requested_limit.min(30) as usize;
        assert_eq!(actual_limit, 30);

        // Verify we can query with the capped limit
        let pools: Vec<_> = POOLS
            .range(deps.as_ref().storage, None, None, Order::Ascending)
            .take(actual_limit)
            .collect::<StdResult<Vec<_>>>()
            .unwrap();
        assert_eq!(pools.len(), 30);
    }

    // ==================== Augmented Fee Tests ====================

    #[test]
    fn test_authorized_creators_multiple() {
        let mut deps = mock_dependencies();
        let tokenlaunch_addr = "tokenlaunch_module";

        // Setup config with two authorized creators
        let config = Config {
            authorized_creators: vec![
                Addr::unchecked(BONDING_CURVE),
                Addr::unchecked(tokenlaunch_addr),
            ],
            swap_fee_bps: 100,
            max_wallet_bps: 300,
        };
        CONFIG.save(deps.as_mut().storage, &config).unwrap();

        let loaded = CONFIG.load(deps.as_ref().storage).unwrap();
        assert_eq!(loaded.authorized_creators.len(), 2);
        assert!(loaded.authorized_creators.contains(&Addr::unchecked(BONDING_CURVE)));
        assert!(loaded.authorized_creators.contains(&Addr::unchecked(tokenlaunch_addr)));
    }

    #[test]
    fn test_augmented_fee_calculation() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Setup pool with augmented fee
        let token_addr = Addr::unchecked(TOKEN);
        let pool = Pool {
            token_address: token_addr.clone(),
            bwick_reserve: 10000,
            token_reserve: 10000,
            lp_token_address: Addr::unchecked("contract"),
            lp_total_supply: 10000,
            locked_lp_supply: 0,
            locked_lp_unlock_at: None,
            locked_lp_recipient: None,
            augmented_fee: Some(AugmentedFeeConfig {
                augmented_fee_bps: 100, // 1% augmented fee
                lp_target_ubwick: 100000,
                active: true,
            }),
        };
        POOLS.save(deps.as_mut().storage, &token_addr, &pool).unwrap();

        // Calculate swap with augmented fee
        // Base fee: 100 bps (1%), Augmented fee: 100 bps (1%), Total: 200 bps (2%)
        let input_amount = 1000u128;
        let total_fee_bps = 200u16; // base + augmented
        let (output, total_fee) = calculate_swap_output(10000, 10000, input_amount, total_fee_bps).unwrap();

        // Expected: fee = 1000 * 200 / 10000 = 20
        // input_after_fee = 1000 - 20 = 980
        // output = 10000 * 980 / (10000 + 980) = 9800000 / 10980 = 892
        assert_eq!(total_fee, 20);
        assert_eq!(output, 892);
    }

    #[test]
    fn test_augmented_fee_auto_disable() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Setup pool with augmented fee close to target
        let token_addr = Addr::unchecked(TOKEN);
        let pool = Pool {
            token_address: token_addr.clone(),
            bwick_reserve: 9900, // pool value = 9900 * 2 = 19800
            token_reserve: 10000,
            lp_token_address: Addr::unchecked("contract"),
            lp_total_supply: 10000,
            locked_lp_supply: 0,
            locked_lp_unlock_at: None,
            locked_lp_recipient: None,
            augmented_fee: Some(AugmentedFeeConfig {
                augmented_fee_bps: 100,
                lp_target_ubwick: 20000, // target is 20000
                active: true,
            }),
        };
        POOLS.save(deps.as_mut().storage, &token_addr, &pool).unwrap();

        // Simulate swap that pushes pool value over target
        // Add 100 BWICK -> pool value becomes (9900 + 100) * 2 = 20000
        let mut pool = POOLS.load(deps.as_ref().storage, &token_addr).unwrap();
        pool.bwick_reserve += 100;

        // Check auto-disable condition
        let pool_value = pool.bwick_reserve * 2;
        assert!(pool_value >= pool.augmented_fee.as_ref().unwrap().lp_target_ubwick);

        // Auto-disable should trigger
        if let Some(ref mut aug_fee) = pool.augmented_fee {
            if aug_fee.active && pool_value >= aug_fee.lp_target_ubwick {
                aug_fee.active = false;
            }
        }

        assert!(!pool.augmented_fee.as_ref().unwrap().active);
    }

    #[test]
    fn test_pool_without_augmented_fee() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Setup normal pool without augmented fee
        let token_addr = Addr::unchecked(TOKEN);
        let pool = Pool {
            token_address: token_addr.clone(),
            bwick_reserve: 10000,
            token_reserve: 10000,
            lp_token_address: Addr::unchecked("contract"),
            lp_total_supply: 10000,
            locked_lp_supply: 0,
            locked_lp_unlock_at: None,
            locked_lp_recipient: None,
            augmented_fee: None,
        };
        POOLS.save(deps.as_mut().storage, &token_addr, &pool).unwrap();

        // Verify swap uses only base fee (100 bps = 1%)
        let input_amount = 1000u128;
        let base_fee_bps = 100u16;
        let (output, fee) = calculate_swap_output(10000, 10000, input_amount, base_fee_bps).unwrap();

        // Expected: fee = 1000 * 100 / 10000 = 10
        // output = 10000 * 990 / (10000 + 990) = 9900000 / 10990 = 900
        assert_eq!(fee, 10);
        assert_eq!(output, 900);
    }

    #[test]
    fn test_augmented_fee_query_status() {
        let mut deps = mock_dependencies();
        setup_contract_state(deps.as_mut());

        // Pool with active augmented fee
        let token_addr = Addr::unchecked(TOKEN);
        let pool = Pool {
            token_address: token_addr.clone(),
            bwick_reserve: 5000, // pool value = 10000
            token_reserve: 10000,
            lp_token_address: Addr::unchecked("contract"),
            lp_total_supply: 10000,
            locked_lp_supply: 0,
            locked_lp_unlock_at: None,
            locked_lp_recipient: None,
            augmented_fee: Some(AugmentedFeeConfig {
                augmented_fee_bps: 200, // 2%
                lp_target_ubwick: 20000,
                active: true,
            }),
        };
        POOLS.save(deps.as_mut().storage, &token_addr, &pool).unwrap();

        let status = query_augmented_fee_status(deps.as_ref(), TOKEN.to_string()).unwrap();
        assert_eq!(status.active, true);
        assert_eq!(status.augmented_fee_bps, 200);
        assert_eq!(status.lp_target_ubwick, Uint128::from(20000u128));
        assert_eq!(status.current_pool_value_ubwick, Uint128::from(10000u128));
        assert_eq!(status.progress_percent, "50.00"); // 10000/20000 * 100 = 50%
    }

    // ==================== Fee Cap Validation Tests ====================

    #[test]
    fn test_instantiate_fee_cap_rejects_over_1000() {
        // Base swap_fee_bps > 1000 should be rejected
        // Test the validation logic directly
        let fee: u16 = 1001;
        assert!(fee > MAX_TOTAL_FEE_BPS);
    }

    #[test]
    fn test_instantiate_fee_cap_allows_1000() {
        // Base swap_fee_bps == 1000 (10%) should be allowed
        let fee: u16 = 1000;
        assert!(fee <= MAX_TOTAL_FEE_BPS);
    }

    #[test]
    fn test_instantiate_fee_cap_allows_zero() {
        // 0% fee should be allowed
        let fee: u16 = 0;
        assert!(fee <= MAX_TOTAL_FEE_BPS);
    }

    #[test]
    fn test_total_fee_cap_base_plus_augmented_at_limit() {
        // Base 900 bps + augmented 100 bps = 1000 bps (exactly at cap, should pass)
        let base_fee: u16 = 900;
        let augmented_fee: u16 = 100;
        let total = base_fee + augmented_fee;
        assert!(total <= MAX_TOTAL_FEE_BPS);
        assert_eq!(total, 1000);
    }

    #[test]
    fn test_total_fee_cap_base_plus_augmented_over_limit() {
        // Base 900 bps + augmented 101 bps = 1001 bps (over cap, should fail)
        let base_fee: u16 = 900;
        let augmented_fee: u16 = 101;
        let total = base_fee + augmented_fee;
        assert!(total > MAX_TOTAL_FEE_BPS);
    }

    #[test]
    fn test_total_fee_cap_typical_config() {
        // Typical config: 100 bps base + 100 bps augmented = 200 bps (well under cap)
        let base_fee: u16 = 100;
        let augmented_fee: u16 = 100;
        let total = base_fee + augmented_fee;
        assert!(total <= MAX_TOTAL_FEE_BPS);
        assert_eq!(total, 200);
    }

    #[test]
    fn test_total_fee_cap_max_augmented_with_base() {
        // Max individual augmented (500) + reasonable base (100) = 600 bps (under cap)
        let base_fee: u16 = 100;
        let augmented_fee: u16 = 500;
        let total = base_fee + augmented_fee;
        assert!(total <= MAX_TOTAL_FEE_BPS);
    }

    #[test]
    fn test_total_fee_cap_max_augmented_with_high_base() {
        // Max individual augmented (500) + high base (501) = 1001 bps (over cap)
        // Note: augmented_fee_bps <= 500 check passes, but total fee cap fails
        let base_fee: u16 = 501;
        let augmented_fee: u16 = 500;
        let total = base_fee + augmented_fee;
        assert!(total > MAX_TOTAL_FEE_BPS);
    }

    #[test]
    fn test_fee_cap_error_has_descriptive_fields() {
        // Verify the error type carries the right information
        let err = ContractError::TotalFeeCapExceeded {
            swap_fee_bps: 600,
            augmented_fee_bps: 500,
            total: 1100,
        };
        let msg = format!("{}", err);
        assert!(msg.contains("1000"));  // mentions the cap
        assert!(msg.contains("600"));   // mentions the base fee
        assert!(msg.contains("500"));   // mentions the augmented fee
        assert!(msg.contains("1100"));  // mentions the total
    }

}