Skip to content

Conversation

@faheelsattar
Copy link
Collaborator

@faheelsattar faheelsattar commented Oct 7, 2025

📝 Summary

Allows sending multiple getHeader requests within a slot and returns the freshest successful header. This helps improving bid while respecting deadlines. It maximizes chance of a higher bid since later requests can return improved headers. It achieves this via YAML config file which can be passed via --relay-config , allowing delayed first requests via TargetFirstRequestMs and multiple subsequent requests at intervals via FrequencyGetHeaderMs. This feature is only activated if EnableTimingGames is set to true in the config file for a particular relay.

This PR also allows setting relays via YAML file where timing games parameters can be set. This is also backwards compatible if someone doesnt want to set relays via YAML file and want to use the default method of setting relays via --relay flag

example config

# timeout settings for get_header requests 
timeout_get_header_ms: 900  # timeout for get_header request in milliseconds
late_in_slot_time_ms: 1000  # threshold that defines when in a slot is considered "too late"

relays:
  # relay with timing games enabled
  - url: "https://0x9000009807ed12c1f08bf4e81c6da3ba8e3fc3d953898ce0102433094e5f22f21102ec057841fcb81978ed1ea0fa8246@relay.relayer1.net"
    # id for identifying the relay. if not provided, the url will be used as the id
    id: relay.relayer1.net
    enable_timing_games: true
    # time in ms from slot start for the first getHeader request
    target_first_request_ms: 200
    # time in ms between subsequent getHeader requests
    frequency_getheader_ms: 100

  # relay with timing games disabled (standard behavior)
  - url: "https://0x9000009807ed12c1f08bf4e81c6da3ba8e3fc3d953898ce0102433094e5f22f21102ec057841fcb81978ed1ea0fa8246@relay.relayer2.com"
    id: relay.relayer2.net
    enable_timing_games: false
    target_first_request_ms: 0
    frequency_getheader_ms: 0

Closes #830

⛱ Motivation and Context

Timing games around bids are a double-edged sword. They can maximize yield for involved parties but also cause more latency (later block proposal within the slot -> more time to include TXs) for the network. Due to sophistication of builders and relays they have already become the norm in the PBS pipeline.
This PR simply provides feature parity with other sidecar implementations and it's still up to proposers whether to opt-in to these advanced features.

📚 References


✅ I have run these commands

  • make lint
  • make test-race
  • go mod tidy

@metachris metachris requested a review from Copilot October 9, 2025 07:24
Copy link

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

This PR adds a timing games feature for bid optimization that allows sending multiple getHeader requests within a slot to maximize the chance of receiving higher bids. The feature is configurable via YAML configuration files and remains backwards compatible with existing relay configuration methods.

Key changes:

  • Introduces RelayConfig struct to wrap RelayEntry with timing games parameters
  • Implements timing games logic with configurable delays and request frequencies
  • Adds YAML configuration support for relay timing settings

Reviewed Changes

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

Show a summary per file
File Description
server/types/relay_entry.go Adds RelayConfig struct with timing games parameters
server/service.go Updates BoostService to use RelayConfig instead of RelayEntry
server/get_header.go Implements timing games logic with multiple timed requests
cli/main.go Adds relay config file loading and merging functionality
cli/flags.go Adds --relay-config flag for YAML configuration
cli/config.go Implements YAML config parsing and relay merging logic
examples/relay_config.yml Provides example configuration file
go.mod Promotes yaml.v3 from indirect to direct dependency

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@potuz
Copy link

potuz commented Oct 9, 2025

Doesn't this feature require CL client implementation changes to not timeout on the request on the CL side?

@faheelsattar
Copy link
Collaborator Author

Doesn't this feature require CL client implementation changes to not timeout on the request on the CL side?

I think the CL clients sends an optional X-Timeout-Ms header which this feature respects.
https://github.com/ethereum/builder-specs/blob/db7c5ee0363c6449997e2a1d5bc9481ed87ba223/apis/builder/header.yaml#L46-#L57

@potuz
Copy link

potuz commented Oct 9, 2025

CLs have a hardcoded timeout for the return of the header from the call to MeV-Boost, I do not see how this works without modifying the CL

@faheelsattar
Copy link
Collaborator Author

CLs have a hardcoded timeout for the return of the header from the call to MeV-Boost, I do not see how this works without modifying the CL

That is okay too since we also have TimeoutGetHeaderMs which will be configurable when setting up mev-boost.
https://github.com/faheelsattar/mev-boost/blob/feat/timing-games/server/get_header.go#L30
https://github.com/faheelsattar/mev-boost/blob/feat/timing-games/server/get_header.go#L85-#L99

@potuz
Copy link

potuz commented Oct 9, 2025

What I mean is that the CL has a hardcoded timeout that is not configurable.

@faheelsattar
Copy link
Collaborator Author

faheelsattar commented Oct 9, 2025

What I mean is that the CL has a hardcoded timeout that is not configurable.

Yeah i understand what you are saying "the CL clients have hardcoded timeouts (e.g: 1000ms) that isnt configurable".
This feature is meant to work within that constraint.
How? cuz we have a configurable value in our mev-boost config file called 'timeout_get_header_ms' (e.g: 900ms) which has to be configured less then the "hardcoded value" in the CL. The operators must configure the values when running mev-boost accordingly. Hope that makes sense.

@potuz
Copy link

potuz commented Oct 14, 2025

This can already be (and is) done at the relay level, I still don't see the importance of having this at the middleware level. Anyway hopefully Gloas minimizes this to at most 200-300ms


type RelayConfigYAML struct {
URL string `yaml:"url"`
ID string `yaml:"id"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do relays need an ID outside the URL?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

optionally if you want. URLs can be too long when being read in metrics so you can essentially give an alias

Copy link
Collaborator

Choose a reason for hiding this comment

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

in other places, just the hostname was a good enough ID and short one. seems better to use only the hostname for metrics too?

@OisinKyne
Copy link

I think the CL clients sends an optional X-Timeout-Ms header which this feature respects.
ethereum/builder-specs@db7c5ee/apis/builder/header.yaml#L46-#L57

I'm pretty sure this is just on the mev-boost side. I don't think any CLs send this header to the sidecar, nor does the sidecar take the header from that request and re-use it upstream.

@metachris metachris changed the title Add timing games feature for bid optimization Add timing feature for bid optimization Oct 25, 2025
@metachris
Copy link
Collaborator

Why does the ID field in the config exist?

I think it may not be needed, and MEV-Boost should simply use the full hostname as short version, as it does in other places, not the full URL.

@metachris
Copy link
Collaborator

@potuz we had multiple conversations with node operators, which all require such a feature to be present to not switch to other implementations that have this (such as commit boost).

@potuz
Copy link

potuz commented Oct 25, 2025

@metachris I guess it's fine, I don't care much cause hopefully this becomes irrelevant in the next fork, but what I am pointing out is that the relays can (and already do) this, and adding this to mev boost itself is kinda irrelevant in this sense, my reading of this is that mev boost will force a relay that does not do timing games to actually do it.

I would be worried that adding a delay in the middleware and misscomunication with the relay adding an extra one may cause missed blocks because of breaking the deadline in the CL. I hope these changes get actually tested in deployed environment with real relays and not some mock flashbots builder run by pandaOps.

}

timeoutGetHeaderMs := config.TimeoutGetHeaderMs
if timeoutGetHeaderMs == 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

neeed to clearly specify this in a documentaton on what the default value is. you can do it in the config.yaml


lateInSlotTimeMs := config.LateInSlotTimeMs
if lateInSlotTimeMs == 0 {
lateInSlotTimeMs = 1000
Copy link
Collaborator

Choose a reason for hiding this comment

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

same for this.

@@ -0,0 +1,24 @@
# Example configuration for mev-boost
Copy link
Collaborator

Choose a reason for hiding this comment

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

would recommend to create a config.example.yaml

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.

Add support for timing games features

6 participants