Skip to content

feat: add strict priority fee ordering mode for backrun bundles#410

Merged
dvush merged 3 commits intomainfrom
backrun_bundles_fix_prio_fee
Mar 5, 2026
Merged

feat: add strict priority fee ordering mode for backrun bundles#410
dvush merged 3 commits intomainfrom
backrun_bundles_fix_prio_fee

Conversation

@dvush
Copy link
Contributor

@dvush dvush commented Feb 26, 2026

📝 Summary

Introduces --backruns.enforce_strict_priority_fee_ordering flag that, when set, requires backrun priority fees to exactly match the target tx fee (instead of >=), sorts candidates by declared coinbase_profit rather than estimated priority fee, and rejects backruns whose actual coinbase profit is less than declared.

💡 Motivation and Context

For some chains we need to strictly keep priority fee ordering so we
require backruns to match the target transaction's priority fee. To
select better backruns we use coinbase profit (it's expected that searchers will send ETH directly to coinbase). We don't want to simulate bundles to get their coinbase profit so we assume accurate values from
the RPC but verify them when we include the bundle to minimize abuse of ordering.


✅ I have completed the following steps:

  • Run make lint
  • Run make test
  • Added tests (if applicable)

Introduces `--backruns.enforce_strict_priority_fee_ordering` flag that,
when enabled, requires backrun priority fees to exactly match the target
tx fee (instead of >=), sorts candidates by declared `coinbase_profit`
rather than estimated priority fee, and rejects backruns whose actual
coinbase profit is less than declared.
Copilot AI review requested due to automatic review settings February 26, 2026 17:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a strict backrun-bundle ordering mode that enforces exact priority-fee matching and uses declared coinbase_profit for candidate ordering, with commit-time validation to prevent overstated profits.

Changes:

  • Introduce --backruns.enforce_strict_priority_fee_ordering and plumb it through builder config, global/payload pools, and RPC wiring.
  • Extend backrun bundle RPC/pool data model with optional coinbase_profit, and sort by it in strict mode.
  • Add strict-mode tests and new execution result for coinbase-profit rejection.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/op-rbuilder/src/backrun_bundle/args.rs Adds CLI flag for strict priority-fee ordering mode.
crates/op-rbuilder/src/backrun_bundle/mod.rs Updates module docs to describe strict ordering behavior.
crates/op-rbuilder/src/backrun_bundle/rpc.rs Adds coinbaseProfit RPC arg, enforces it in strict mode, and passes through to stored bundle.
crates/op-rbuilder/src/backrun_bundle/payload_pool.rs Adds coinbase_profit to stored bundles; changes ordering to use coinbase profit in strict mode; adds strict fee prefiltering.
crates/op-rbuilder/src/backrun_bundle/global_pool.rs Makes global pool aware of strict-mode setting when creating payload pools.
crates/op-rbuilder/src/backrun_bundle/test_utils.rs Extends backrun bundle test builder to set coinbase_profit.
crates/op-rbuilder/src/builders/mod.rs Wires strict-mode flag into BackrunBundleGlobalPool construction.
crates/op-rbuilder/src/builders/context.rs Enforces strict fee equality and validates actual vs declared coinbase profit during backrun commit.
crates/op-rbuilder/src/launcher.rs Passes strict-mode flag into BackrunBundleRpc::new.
crates/op-rbuilder/src/tests/framework/instance.rs Passes strict-mode flag into test instance RPC wiring.
crates/op-rbuilder/src/tests/framework/txs.rs Extends test bundle opts with coinbase_profit and sends it for backrun bundles.
crates/op-rbuilder/src/tests/backrun.rs Adds integration tests covering strict mode rejection/acceptance and ordering by coinbase_profit.
crates/op-rbuilder/src/primitives/reth/execution.rs Adds CoinbaseProfitTooLow execution result variant.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

let priority = if use_coinbase_profit {
bundle.coinbase_profit.unwrap_or_default()
} else {
U256::from(bundle.estimated_effective_priority_fee)
Copy link
Collaborator

Choose a reason for hiding this comment

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

are they reinserted every block? Or it would be static once bundle created?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good question, I think it might change

on the builder side we can remove on hash collision just in case.

but I would expect simulator to send to the non-overlapping block ranges if re simulation happens

Copy link
Collaborator

@SozinM SozinM Mar 5, 2026

Choose a reason for hiding this comment

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

my point is that on next block effective fee could change and this priority needs to be updated

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we can enforce that you can have only one (target tx, backrun tx) pair at the same time. I need to think a bit about it and if we add this this is IMO better to do as a separate PR since I need functions from the cancellation PR.

Copilot AI review requested due to automatic review settings March 5, 2026 11:03
@dvush dvush enabled auto-merge (squash) March 5, 2026 11:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 82 to 85
impl PartialEq for OrderedBackrunBundle {
fn eq(&self, other: &Self) -> bool {
self.backrun_tx_hash() == other.backrun_tx_hash()
self.backrun_tx_hash() == other.backrun_tx_hash() && self.priority == other.priority
}
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

OrderedBackrunBundle equality/uniqueness is now based on (backrun_tx_hash, priority) rather than just backrun_tx_hash. Because the BTreeSet ordering also uses priority first, the same signed backrun tx can be inserted multiple times with different declared coinbase_profit/estimated fee, and remove_bundle / replacement cleanup will only remove the matching priority variant. Consider enforcing uniqueness by backrun tx hash (e.g., make Ord/Eq treat identical hashes as equal, or switch to a map keyed by hash and derive ordering at query time).

Copilot uses AI. Check for mistakes.
Comment on lines 201 to +206
tx_backruns
.bundles
.iter()
.take(max_iter)
.filter(|ordered| {
let backrun_tx = &ordered.0.backrun_tx;
let backrun_tx = &ordered.bundle.backrun_tx;
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

get_backruns truncates the candidate set with .iter().take(max_iter) before applying the strict priority-fee filter. In strict mode, bundles are ordered by untrusted coinbase_profit, so a large number of high-profit but fee-mismatched bundles can crowd out fee-matching ones beyond max_iter, causing valid backruns to be missed (and enabling cheap starvation/DoS). Consider restructuring selection to ensure fee-matching candidates are discoverable within the scan budget (e.g., filter by effective fee first, then rank by coinbase_profit, or increase/adapt the scan limit in strict mode).

Copilot uses AI. Check for mistakes.
}

if self.backrun_ctx.args.enforce_strict_priority_fee_ordering {
let stated = bundle.coinbase_profit.unwrap_or_default();
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

In strict mode, stated coinbase profit is derived via bundle.coinbase_profit.unwrap_or_default(), so a missing coinbase_profit silently becomes 0 and will always pass the actual < stated check. This contradicts the strict-mode contract enforced at the RPC layer and the module docs (“Required when enforce_strict_priority_fee_ordering is enabled”). Consider treating None as invalid here (log and skip) to keep the invariants consistent even if bundles enter the pool through non-RPC paths.

Suggested change
let stated = bundle.coinbase_profit.unwrap_or_default();
let Some(stated) = bundle.coinbase_profit else {
// In strict mode, a missing stated coinbase profit is invalid.
// Log and skip to keep invariants consistent with RPC-layer checks.
log_br_txn(TxnExecutionResult::CoinbaseProfitTooLow);
continue;
};

Copilot uses AI. Check for mistakes.
@dvush dvush merged commit 895158a into main Mar 5, 2026
8 checks passed
@dvush dvush deleted the backrun_bundles_fix_prio_fee branch March 5, 2026 11:11
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.

3 participants