drift-labs/protocol-v2

code too complex - should modularize the program code (case study: `fill_perp_order`)

0xbigz opened this issue · 0 comments

0xbigz commented

Here is a high-level suggestion of how you can organize the function in a more modular way. Please note this is pseudo code and may not reflect the exact syntax and semantics of Rust:

  1. validate_order_and_market(): This function would encapsulate all the validations currently scattered throughout the fill_perp_order() function. This might include checks for the order market type, whether the market is active, whether the order is open, etc.

  2. settle_lp_and_update_funding_rate(): This function would handle the logic related to settling lp and updating the funding rate. It could also handle the logic related to checking if the market is paused.

  3. get_oracle_and_market_info(): This function would encapsulate the logic related to getting oracle and market information like oracle price, reserve price, etc.

  4. get_maker_and_referrer_info(): This function would handle the logic related to maker orders and referrer information.

  5. cancel_order_if_needed(): This function would handle the logic related to checking if an order should be expired or canceled.

  6. fulfill_perp_order_and_update_user_state(): This function would handle the logic related to fulfilling perp order and updating the user state.

  7. validate_market_and_update_last_active_slot(): This function would handle the logic related to validating the market and updating the last active slot of the user.

Your fill_perp_order() function could then look something like this:

pub fn fill_perp_order(...) -> DriftResult<(u64, bool)> {
    let now = clock.unix_timestamp;
    let slot = clock.slot;
    let filler_key = filler.key();
    let user_key = user.key();

    let user = &mut load_mut!(user)?;
    let user_stats = &mut load_mut!(user_stats)?;

    let order_index = find_order_index(user, order_id)?;

    validate_order_and_market(user, user_stats, perp_market_map, oracle_map, state, order_index, now)?;

    settle_lp_and_update_funding_rate(user, user_key, perp_market_map, market_index, now)?;

    let (reserve_price_before, oracle_price, is_oracle_valid) = get_oracle_and_market_info(perp_market_map, oracle_map, market_index, state)?;

    let (filler, filler_stats) = get_filler_and_filler_stats(user, filler, filler_stats, filler_key)?;

    let (maker_orders_info, referrer_info) = get_maker_and_referrer_info(...);  // pass necessary arguments

    cancel_order_if_needed(...); // pass necessary arguments

    let (base_asset_amount, potentially_risk_increasing, mut updated_user_state) = fulfill_perp_order_and_update_user_state(...); // pass necessary arguments

    validate_market_and_update_last_active_slot(user, user_key, perp_market_map, oracle_map, state, market_index, oracle_reserve_price_spread_pct_before, potentially_risk_increasing, now, slot)?;

    Ok((base_asset_amount, updated_user_state))
}

Remember, this is just a high-level suggestion, and you might need to adjust it based on your specific needs and constraints. Also, each newly created function should have clear and concise documentation, so it's easy to understand what each function does.