Skip to content

fix(pool): apply the sponsorship budget gate when counting candidates - #1336

Merged
inakov merged 1 commit into
mainfrom
ivan/pool-num-candidates-sponsorship-budget
Sep 10, 2026
Merged

inakov merged 1 commit into
mainfrom
ivan/pool-num-candidates-sponsorship-budget

Conversation

@inakov

@inakov inakov commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Task 1 of the Rundler Production Task handoff doc. Metric-only: no change to which ops are eligible, returned to the assigner, or bundled.

Motivation

On rundler-robinhood-mainnet EP v0.7, rundler_op_pool_num_candidates read 317 while rundler_builder_assigner_ep_eligible_ops for the same entrypoint read 0. Every one of those ops was bundler-sponsored with a budget that no longer covered the current gas price, so the assigner rejected it on every pass. The pool's candidate check exempted sponsored ops from any price gate, so it counted them as bundleable indefinitely. That made the dashboard's num_ops_in_pool - num_candidates "stuck ops" panel read ~0 while essentially the whole pool was stuck.

Proposed Changes

  • Replace the sponsored-op exemption in do_maintenance with the assigner's budget gate (op_meets_fee_requirements): a sponsored op is a candidate only while total_gas_limit * required_max_fee_per_gas <= max_cost. Non-sponsored ops keep the existing fee comparison.
  • Route over-budget sponsored ops through set_underpriced() like non-sponsored underpriced ops. set_underpriced() only clears the time-to-mine tracker, so the op stays eligible and in best_operations. Side benefit: stranded sponsored ops no longer accrue days of time-to-mine that would skew the histogram if they eventually mine.
  • do_maintenance returns the candidate count so tests can assert on it directly. The single caller ignores the value.
  • Tests: sponsored op within budget is a candidate; one wei over budget is not, stays in the pool and in best_operations, and has its tracker cleared; a blocked op recovers when fees drop (re-evaluated each pass, not latched); non-sponsored ops unchanged.

Notes

  • The pool uses state.uo_fees and the assigner uses its own required_fees. Both come from fee_estimator.required_op_fees(bundle_fees), sampled at slightly different times, so the two metrics should now track each other closely rather than exactly.
  • The DA-gas / PVG eligibility path above this check still skips sponsored ops. Unchanged here, as the doc specifies.

Expected effect

num_candidates on robinhood EP v0.7 drops from ~317 to roughly the count of genuinely bundleable ops and moves together with ep_eligible_ops. The num_ops_in_pool - num_candidates dashboard panel becomes a usable stuck-ops signal.

Related: #1335 (Task 3, log spam). Task 2 (num_suspect_ops drift) will follow separately.

@codecov

codecov Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.68%. Comparing base (326e061) to head (b42ba8f).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

Files with missing lines Coverage Δ
crates/pool/src/mempool/pool.rs 97.81% <100.00%> (+0.09%) ⬆️

... and 1 file with indirect coverage changes

Flag Coverage Δ
unit-tests 63.68% <100.00%> (+0.08%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
rundler binary 10.26% <ø> (ø)
builder 76.67% <ø> (+<0.01%) ⬆️
dev ∅ <ø> (∅)
pool 67.04% <100.00%> (+0.27%) ⬆️
provider 30.90% <ø> (ø)
rpc 46.29% <ø> (ø)
sim 79.67% <ø> (ø)
tasks ∅ <ø> (∅)
types 71.72% <ø> (ø)
utils 53.40% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

num_candidates exempted bundler-sponsored ops from the price check, so a
sponsored op whose budget no longer covers the required fee was counted
as a candidate indefinitely. On robinhood EP v0.7 the gauge read 317
while the assigner's ep_eligible_ops read 0.

Mirror the assigner's gate (op_meets_fee_requirements) in do_maintenance:
a sponsored op is a candidate only while total_gas_limit * required
max_fee_per_gas fits within its max_cost. Over-budget ops go through
set_underpriced() like non-sponsored underpriced ops, which also stops
them accruing time-to-mine while stranded. Metric-only: the op stays
eligible and in best_operations.

do_maintenance now returns the candidate count so tests can assert on it.
@inakov
inakov force-pushed the ivan/pool-num-candidates-sponsorship-budget branch from 818ffbc to b42ba8f Compare September 10, 2026 15:33
@inakov
inakov disabled the stack merge September 10, 2026 15:34
@inakov
inakov merged commit 7e2bc64 into main Sep 10, 2026
15 checks passed
@inakov
inakov deleted the ivan/pool-num-candidates-sponsorship-budget branch September 10, 2026 15:34
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