Add onchain gas cost to trades and orders APIs#4540
Conversation
Persist the actual settlement gas (gas_used, effective_gas_price) read from the transaction receipt — previously computed by the autopilot but only logged since the settlement_observations table was dropped in V090 — and expose a per-trade `gasCost` (native token wei). Attribution: each trade gets its share of the settlement transaction's gas cost (gas_used * effective_gas_price), split equally across all trades settled in the same transaction. - V111 migration: nullable gas_used + effective_gas_price on settlements - autopilot save_settlement writes them (going-forward only, no backfill) - trades query computes per-trade gas_cost in SQL - model + openapi documentation for the new gasCost field on trades Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expose a per-order `gasCost` (native token wei): the sum, across the order's fills, of each trade's share of its settlement transaction's gas cost (gas_used * effective_gas_price), split equally across all trades settled in the same transaction. Builds on the settlement gas now persisted for trades. - order_gas_costs aggregates the per-trade gas attribution per order_uid - orders wired on the single_order / many_orders read paths - model + openapi documentation for the new gasCost field on orders Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Reminder: Please update the DB Readme and comment whether migrations are reversible (include rollback scripts if applicable).
Caused by: |
|
Reminder: Please consider backward compatibility when modifying the API specification.
Caused by: |
|
Claude finished @jmg-duarte's task in 4m 13s —— View job PR Review: Add on-chain gas cost to trades and orders APIsI reviewed the diff against I left 3 inline comments:
The doc-comment mismatches (1 & 2) read like leftovers from an earlier quote-based design — worth fixing since they directly contradict the OpenAPI and the implementation. Minor (non-blocking)
|
There was a problem hiding this comment.
Code Review
This pull request implements tracking and persistence of actual on-chain gas costs for settlements, trades, and orders, including database migrations, query updates, and OpenAPI documentation. The reviewer feedback highlights that the docstrings for the gas_cost fields in both the OrderMetadata and Trade models are incorrect and misleading, as they describe the value as an estimated cost derived from quotes rather than the actual on-chain gas cost.
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.
| /// Estimated network gas cost (in native token wei) of executing this | ||
| /// order, derived from its quote (`gas_amount * gas_price`). `None` when | ||
| /// the order has no stored quote. | ||
| #[serde_as(as = "Option<HexOrDecimalU256>")] | ||
| #[serde(default, skip_serializing_if = "Option::is_none")] | ||
| pub gas_cost: Option<U256>, |
There was a problem hiding this comment.
The docstring for gas_cost is incorrect and misleading. It states that the gas cost is an "Estimated network gas cost... derived from its quote", but the PR actually implements and exposes the actual on-chain gas cost attributed to the order (as documented in the OpenAPI spec and implemented in the database queries). Please update the docstring to accurately reflect that this is the actual on-chain gas cost.
| /// Estimated network gas cost (in native token wei) of executing this | |
| /// order, derived from its quote (`gas_amount * gas_price`). `None` when | |
| /// the order has no stored quote. | |
| #[serde_as(as = "Option<HexOrDecimalU256>")] | |
| #[serde(default, skip_serializing_if = "Option::is_none")] | |
| pub gas_cost: Option<U256>, | |
| /// Actual on-chain gas cost (in native token wei) attributed to this | |
| /// order. Computed as the order's share of the gas cost | |
| /// (gas_used * effective_gas_price) of the settlement transaction(s) | |
| /// that executed it, split equally across all trades settled in the same | |
| /// transaction, and summed across the order's fills. None for orders | |
| /// settled before this data started being recorded, or not yet settled. | |
| #[serde_as(as = "Option<HexOrDecimalU256>")] | |
| #[serde(default, skip_serializing_if = "Option::is_none")] | |
| pub gas_cost: Option<U256>, |
| /// Estimated network gas cost (in native token wei) of executing this | ||
| /// trade, derived from the order's quote (`gas_amount * gas_price`). | ||
| /// `None` when the order has no stored quote (e.g. JIT orders). | ||
| #[serde_as(as = "Option<HexOrDecimalU256>")] | ||
| #[serde(default, skip_serializing_if = "Option::is_none")] | ||
| pub gas_cost: Option<U256>, |
There was a problem hiding this comment.
The docstring for gas_cost is incorrect and misleading. It states that the gas cost is an "Estimated network gas cost... derived from the order's quote", but the PR actually implements and exposes the actual on-chain gas cost attributed to the trade. Please update the docstring to accurately reflect that this is the actual on-chain gas cost.
/// Actual on-chain gas cost (in native token wei) attributed to this
/// trade. Computed as this trade's share of the settlement transaction's
/// gas cost (gas_used * effective_gas_price), split equally across all
/// trades settled in the same transaction. None for trades settled
/// before this data started being recorded.
#[serde_as(as = "Option<HexOrDecimalU256>")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gas_cost: Option<U256>,| /// Estimated network gas cost (in native token wei) of executing this | ||
| /// order, derived from its quote (`gas_amount * gas_price`). `None` when | ||
| /// the order has no stored quote. |
There was a problem hiding this comment.
This doc comment is incorrect and contradicts both the implementation and the OpenAPI spec. The value populated into gas_cost comes from database::trades::order_gas_costs, i.e. the actual on-chain settlement gas cost (gas_used * effective_gas_price from the receipt, split across trades) — not the quote (gas_amount * gas_price). The None condition is also wrong: it's None for orders settled before V111 or not yet settled, not "the order has no stored quote".
| /// Estimated network gas cost (in native token wei) of executing this | |
| /// order, derived from its quote (`gas_amount * gas_price`). `None` when | |
| /// the order has no stored quote. | |
| /// Actual on-chain gas cost (in native token wei) attributed to this order: | |
| /// its share of the settlement transaction(s)' gas cost | |
| /// (`gas_used * effective_gas_price`), split equally across all trades in | |
| /// each settlement and summed across the order's fills. `None` for orders | |
| /// settled before this data was recorded (see V111), or not yet settled. |
| /// Estimated network gas cost (in native token wei) of executing this | ||
| /// trade, derived from the order's quote (`gas_amount * gas_price`). | ||
| /// `None` when the order has no stored quote (e.g. JIT orders). |
There was a problem hiding this comment.
Same issue as the OrderMetadata::gas_cost comment: this is the actual on-chain gas cost (this trade's share of the settlement's gas_used * effective_gas_price), not a quote-derived estimate. The "None ... (e.g. JIT orders)" claim is also misleading — JIT orders have trades and settlements, so they do get a gas cost here; the value is None only for trades settled before V111.
| /// Estimated network gas cost (in native token wei) of executing this | |
| /// trade, derived from the order's quote (`gas_amount * gas_price`). | |
| /// `None` when the order has no stored quote (e.g. JIT orders). | |
| /// Actual on-chain gas cost (in native token wei) attributed to this trade: | |
| /// its share of the settlement transaction's gas cost | |
| /// (`gas_used * effective_gas_price`), split equally across all trades | |
| /// settled in the same transaction. `None` for trades settled before this | |
| /// data was recorded (see V111). |
| ALTER TABLE settlements | ||
| ADD COLUMN gas_used numeric(78, 0), | ||
| ADD COLUMN effective_gas_price numeric(78, 0); |
There was a problem hiding this comment.
Deploy-ordering caveat worth confirming: the orderbook trades / order_gas_costs queries now reference s.gas_used and s.effective_gas_price unconditionally. If a new orderbook pod starts serving before this migration has been applied (the migration is run by autopilot's Flyway, a separate deployment), every trades/orders query will fail with column "gas_used" does not exist. Please make sure V111 is applied before (or atomically with) the new orderbook rollout — see the bot's backward-compatibility reminder above.
Description
The removal of UCP means that we no longer have fees and gas costs info in a single place, meaning the FE must calculate them; the issue comes when one notices that we don't keep track of gas costs for settlements (meaning trades and orders don't either).
This PR adds gas cost tracking, when settlements get indexed, they will now be indexed with the gas cost.
The PR does not do backfills on purpose, that must be done as a separate process.
Changes
How to test
Sepolia staging is currently suspended with this code there, please do a trade and review the UI using this link
https://jmgd-ucp.explorer-dev-dxz.pages.dev/
Under costs and fees you should see this:
