Skip to content

[streaming-quote-api] Extract reusable assemble_quote_data (2/3)#4468

Open
squadgazzz wants to merge 2 commits into
feat/streaming-quote-competitionfrom
feat/streaming-quote-assembly
Open

[streaming-quote-api] Extract reusable assemble_quote_data (2/3)#4468
squadgazzz wants to merge 2 commits into
feat/streaming-quote-competitionfrom
feat/streaming-quote-assembly

Conversation

@squadgazzz
Copy link
Copy Markdown
Contributor

Description

Second of three stacked PRs for the streaming quote API (#4456). Pure refactor, no behavior change. It pulls the "build one quote from one solver estimate" logic out of compute_quote_data into a standalone assemble_quote_data, so the streaming path in the next PR can call it once per emitted solver result. Stacked on #4467.

Changes

  • Extract assemble_quote_data from OrderQuoter::compute_quote_data. The method still computes the quote expiration itself and now delegates the field assembly to the new function.

How to test

Existing tests, plus new unit tests covering the extracted function on both sell and buy sides.

Related issues

Part of #4456

@squadgazzz squadgazzz changed the title Extract reusable assemble_quote_data (2/3) [quote-streaming-api] Extract reusable assemble_quote_data (2/3) Jun 3, 2026
@squadgazzz squadgazzz changed the title [quote-streaming-api] Extract reusable assemble_quote_data (2/3) [streaming-quote-api] Extract reusable assemble_quote_data (2/3) Jun 4, 2026
@squadgazzz squadgazzz marked this pull request as ready for review June 5, 2026 10:54
@squadgazzz squadgazzz requested a review from a team as a code owner June 5, 2026 10:54
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the quote assembly logic by extracting it into a dedicated function, assemble_quote_data, and adds corresponding unit tests. The review feedback highlights that the function should accept the estimate parameter by value rather than by reference to avoid unnecessary cloning of heap-allocated execution metadata, which improves performance.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +487 to +493
let quote = assemble_quote_data(
parameters,
&trade_estimate,
effective_gas_price,
sell_token_price,
};

let quote_kind = quote_kind_from_signing_scheme(&parameters.signing_scheme);
let quote = QuoteData {
sell_token: parameters.sell_token,
buy_token: parameters.buy_token,
quoted_sell_amount,
quoted_buy_amount,
fee_parameters,
kind: trade_query.kind,
expiration,
quote_kind,
solver: trade_estimate.solver,
verified: trade_estimate.verified,
metadata: QuoteMetadataV1 {
interactions: trade_estimate.execution.interactions,
pre_interactions: trade_estimate.execution.pre_interactions,
jit_orders: trade_estimate.execution.jit_orders,
}
.into(),
};
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Pass trade_estimate by value to assemble_quote_data to avoid unnecessary cloning of execution metadata.

        let quote = assemble_quote_data(
            parameters,
            trade_estimate,
            effective_gas_price,
            sell_token_price,
            expiration,
        );
References
  1. Prefer passing a value (by cloning at the call site) over passing a reference if the called function requires ownership and would have to clone the value internally anyway.

Comment on lines +636 to +679
fn assemble_quote_data(
parameters: &QuoteParameters,
estimate: &price_estimation::Estimate,
effective_gas_price: u128,
sell_token_price: f64,
expiration: DateTime<Utc>,
) -> QuoteData {
let (quoted_sell_amount, quoted_buy_amount, kind) = match &parameters.side {
OrderQuoteSide::Sell {
sell_amount: SellAmount::BeforeFee { value: sell_amount },
}
| OrderQuoteSide::Sell {
sell_amount: SellAmount::AfterFee { value: sell_amount },
} => (sell_amount.get(), estimate.out_amount, OrderKind::Sell),
OrderQuoteSide::Buy {
buy_amount_after_fee: buy_amount,
} => (estimate.out_amount, buy_amount.get(), OrderKind::Buy),
};

let fee_parameters = FeeParameters {
gas_amount: estimate.gas as f64,
gas_price: effective_gas_price as f64,
sell_token_price,
};

QuoteData {
sell_token: parameters.sell_token,
buy_token: parameters.buy_token,
quoted_sell_amount,
quoted_buy_amount,
fee_parameters,
kind,
expiration,
quote_kind: quote_kind_from_signing_scheme(&parameters.signing_scheme),
solver: estimate.solver,
verified: estimate.verified,
metadata: QuoteMetadataV1 {
interactions: estimate.execution.interactions.clone(),
pre_interactions: estimate.execution.pre_interactions.clone(),
jit_orders: estimate.execution.jit_orders.clone(),
}
.into(),
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Change assemble_quote_data to accept estimate by value. This avoids cloning interactions, pre_interactions, and jit_orders vectors, which are heap-allocated and can be moved directly.

fn assemble_quote_data(
    parameters: &QuoteParameters,
    estimate: price_estimation::Estimate,
    effective_gas_price: u128,
    sell_token_price: f64,
    expiration: DateTime<Utc>,
) -> QuoteData {
    let (quoted_sell_amount, quoted_buy_amount, kind) = match &parameters.side {
        OrderQuoteSide::Sell {
            sell_amount: SellAmount::BeforeFee { value: sell_amount },
        }
        | OrderQuoteSide::Sell {
            sell_amount: SellAmount::AfterFee { value: sell_amount },
        } => (sell_amount.get(), estimate.out_amount, OrderKind::Sell),
        OrderQuoteSide::Buy {
            buy_amount_after_fee: buy_amount,
        } => (estimate.out_amount, buy_amount.get(), OrderKind::Buy),
    };

    let fee_parameters = FeeParameters {
        gas_amount: estimate.gas as f64,
        gas_price: effective_gas_price as f64,
        sell_token_price,
    };

    QuoteData {
        sell_token: parameters.sell_token,
        buy_token: parameters.buy_token,
        quoted_sell_amount,
        quoted_buy_amount,
        fee_parameters,
        kind,
        expiration,
        quote_kind: quote_kind_from_signing_scheme(&parameters.signing_scheme),
        solver: estimate.solver,
        verified: estimate.verified,
        metadata: QuoteMetadataV1 {
            interactions: estimate.execution.interactions,
            pre_interactions: estimate.execution.pre_interactions,
            jit_orders: estimate.execution.jit_orders,
        }
        .into(),
    }
}
References
  1. Prefer passing a value (by cloning at the call site) over passing a reference if the called function requires ownership and would have to clone the value internally anyway.

execution: Default::default(),
};

let data = assemble_quote_data(&parameters, &estimate, 2, 0.5, expiration);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Pass estimate by value in the test.

        let data = assemble_quote_data(&parameters, estimate, 2, 0.5, expiration);
References
  1. Prefer passing a value (by cloning at the call site) over passing a reference if the called function requires ownership and would have to clone the value internally anyway.

execution: Default::default(),
};

let data = assemble_quote_data(&parameters, &estimate, 2, 0.5, expiration);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Pass estimate by value in the test.

        let data = assemble_quote_data(&parameters, estimate, 2, 0.5, expiration);
References
  1. Prefer passing a value (by cloning at the call site) over passing a reference if the called function requires ownership and would have to clone the value internally anyway.

sell_token: Address::repeat_byte(1),
buy_token: Address::repeat_byte(2),
side: OrderQuoteSide::Sell {
sell_amount: SellAmount::AfterFee {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For optimal test coverage there should also be case for SellAmount::BeforeFee, no?
But since is just moving code into a separate function that gets executed in basically every e2e I'd also be fine with not having any additional tests.
AFAICS the only difference is that the order kind now gets decided by the match statement instead of reading it from the trade query.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants