Skip to content

Conversation

fahimahmedx
Copy link
Member

@fahimahmedx fahimahmedx commented Sep 26, 2025

Summary

Per-address gas limit feature summary — We use a standard token bucket algorithm to rate limit transaction inclusion from senders. Each sender is allocated a gas bucket, and each transaction they send needs to take from that bucket, whether the transaction reverts or not. The buckets refill a little bit every block. If a sender doesn't have sufficient gas for a transaction, then their transaction gets delayed until their bucket refills.

This PR does the following:

  • implements this gas-limiter feature originally from op-rbuilder, integrating the gas_limiter module from op-rbuilder
  • implements filters field to AppendOrders, allowing custom boolean functions to be used for filtering out transactions.
  • integrates the gas_limiter module to be used as a filter in the AppendOrders step.

Todo:

  • Put gas_limit in pool/filters folder.
  • "Think about an interface design that optionally allows devs to add pre/post job logic, while still allowing the minimalistic syntax that we have right now."
  • "Try to find an elegant way to initialize the filter-specific metrics scope at runtime in the setup function. We might need to change the filter interface if there is no clean way of doing it."

Copy link
Collaborator

@karim-agha karim-agha left a comment

Choose a reason for hiding this comment

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

The code looks good and clean, good job. There are few design choices you need to make here.

Also please move this module under /pool/filters/

use crate::gas_limiter::error::GasLimitError;

#[derive(Metrics, Clone)]
#[metrics(scope = "op_rbuilder.gas_limiter")]
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be #[derive(MetricsSet)]

See:

#[derive(MetricsSet)]

Pipeline steps learn about the name of their scope when the pipeline is instantiated. So far, the pattern that has emerged is that metrics are initialized (::with_scope(..)) in Step::setup. Try to find an elegant way to initialize the filter-specific metrics scope at runtime in the setup function. We might need to change the filter interface if there is no clean way of doing it.

Comment on lines +98 to +102
/// Refreshes the gas buckets, typically called at the start of each block.
/// This refills buckets and performs garbage collection of stale entries.
pub fn refresh(&self, block_number: u64) {
self.limiter.refresh(block_number);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Alright... we're seeing an emerging pattern here. Maybe more filters will need to have some logic that kicks in before or after a payload job.

Try to think about an interface design that optionally allows devs to add pre/post job logic, while still allowing the minimalistic syntax that we have right now.

Comment on lines +21 to +23
pub struct AddressGasLimiter {
inner: Option<AddressGasLimiterInner>,
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, maybe this alternative shape of the data structure?

enum AddressGasLimiter {
   Disabled,
   Enabled { config, address_buckets, metrics }
}

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