-
Notifications
You must be signed in to change notification settings - Fork 397
feat(provider): Add eth_sendBundle support to provider #2556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
0da2a7b
feat(provider): Add eth_sendBundle support to provider
cakevm ea8f8d3
Rename to `send_bundle_with_auth`
cakevm 2f92045
Improve wording of doc strings
cakevm f9ea1fb
Use bundle here
cakevm 6ec689c
Remove pubsub requirement
cakevm c5ea811
nit
cakevm 99ef774
Use a extension to set http headers in transport
cakevm d1c2946
Introduce mev builder
cakevm 57d4bc4
Replace panic with error handling
cakevm c29ef7a
Replace expect also with error handling
cakevm 00a21b7
Refactor to use HeaderMap
cakevm 7caa0c6
Simplify if/else branch for fn headers
cakevm 9c7fb62
Use everywhere the standard HeaderMap and document the behaviour
cakevm 42f9c85
Remove unnecessary clone
cakevm ee5b960
Revert error handling for map_meta
cakevm 3039caf
use iterator from HeaderMap
cakevm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
mod with_auth; | ||
|
||
pub use self::with_auth::{sign_flashbots_payload, MevBuilder}; | ||
use crate::Provider; | ||
use alloy_network::Network; | ||
use alloy_rpc_types_mev::{EthBundleHash, EthSendBundle}; | ||
|
||
/// The HTTP header used for Flashbots signature authentication. | ||
pub const FLASHBOTS_SIGNATURE_HEADER: &str = "X-Flashbots-Signature"; | ||
|
||
/// This module provides support for interacting with non-standard MEV-related RPC endpoints. | ||
#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] | ||
#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] | ||
pub trait MevApi<N>: Send + Sync { | ||
/// Sends a MEV bundle using the `eth_sendBundle` RPC method. | ||
/// Returns the resulting bundle hash on success. | ||
fn send_bundle(&self, bundle: EthSendBundle) -> MevBuilder<(EthSendBundle,), EthBundleHash>; | ||
} | ||
|
||
#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))] | ||
#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)] | ||
impl<N, P> MevApi<N> for P | ||
where | ||
N: Network, | ||
P: Provider<N>, | ||
{ | ||
fn send_bundle(&self, bundle: EthSendBundle) -> MevBuilder<(EthSendBundle,), EthBundleHash> { | ||
MevBuilder::new_rpc(self.client().request("eth_sendBundle", (bundle,))) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
use crate::{ext::FLASHBOTS_SIGNATURE_HEADER, ProviderCall}; | ||
use alloy_json_rpc::{HttpHeaderExtension, RpcRecv, RpcSend}; | ||
use alloy_primitives::{hex, keccak256}; | ||
use alloy_rpc_client::RpcCall; | ||
use alloy_signer::Signer; | ||
use alloy_transport::{TransportErrorKind, TransportResult}; | ||
use std::future::IntoFuture; | ||
|
||
/// A builder for MEV RPC calls that allow optional Flashbots authentication. | ||
pub struct MevBuilder<Params, Resp, Output = Resp, Map = fn(Resp) -> Output> | ||
where | ||
Params: RpcSend, | ||
Resp: RpcRecv, | ||
Map: Fn(Resp) -> Output, | ||
{ | ||
inner: RpcCall<Params, Resp, Output, Map>, | ||
signer: Option<Box<dyn Signer + Send + Sync>>, | ||
} | ||
|
||
impl<Params, Resp, Output, Map> std::fmt::Debug for MevBuilder<Params, Resp, Output, Map> | ||
where | ||
Params: RpcSend + std::fmt::Debug, | ||
Resp: RpcRecv + std::fmt::Debug, | ||
Output: std::fmt::Debug, | ||
Map: Fn(Resp) -> Output + Clone + std::fmt::Debug, | ||
{ | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.debug_struct("MevBuilder").field("inner", &self.inner).finish() | ||
} | ||
} | ||
|
||
impl<Params, Resp, Output, Map> MevBuilder<Params, Resp, Output, Map> | ||
where | ||
Params: RpcSend, | ||
Resp: RpcRecv, | ||
Map: Fn(Resp) -> Output + Clone, | ||
{ | ||
/// Create a new [`MevBuilder`] from a [`RpcCall`]. | ||
pub const fn new_rpc(inner: RpcCall<Params, Resp, Output, Map>) -> Self { | ||
Self { inner, signer: None } | ||
} | ||
} | ||
|
||
impl<Params, Resp, Output, Map> From<RpcCall<Params, Resp, Output, Map>> | ||
for MevBuilder<Params, Resp, Output, Map> | ||
where | ||
Params: RpcSend, | ||
Resp: RpcRecv, | ||
Map: Fn(Resp) -> Output + Clone, | ||
{ | ||
fn from(inner: RpcCall<Params, Resp, Output, Map>) -> Self { | ||
Self::new_rpc(inner) | ||
} | ||
} | ||
|
||
impl<Params, Resp, Output, Map> MevBuilder<Params, Resp, Output, Map> | ||
where | ||
Params: RpcSend, | ||
Resp: RpcRecv, | ||
Map: Fn(Resp) -> Output, | ||
{ | ||
/// Enables Flashbots authentication using the provided signer. | ||
/// | ||
/// The signer is used to generate the `X-Flashbots-Signature` header, which will be included | ||
/// in the request if the transport supports HTTP headers. | ||
pub fn with_auth<S: Signer + Send + Sync + 'static>(mut self, signer: S) -> Self { | ||
self.signer = Some(Box::new(signer)); | ||
self | ||
} | ||
} | ||
|
||
impl<Params, Resp, Output, Map> IntoFuture for MevBuilder<Params, Resp, Output, Map> | ||
where | ||
Params: RpcSend + 'static, | ||
Resp: RpcRecv, | ||
Output: 'static, | ||
Map: Fn(Resp) -> Output + Send + 'static, | ||
{ | ||
type Output = TransportResult<Output>; | ||
type IntoFuture = ProviderCall<Params, Resp, Output, Map>; | ||
|
||
fn into_future(self) -> Self::IntoFuture { | ||
if let Some(signer) = self.signer { | ||
let fut = async move { | ||
// Generate the Flashbots signature for the request body | ||
let body = serde_json::to_string(&self.inner.request()) | ||
.map_err(TransportErrorKind::custom)?; | ||
let signature = sign_flashbots_payload(body, &signer) | ||
.await | ||
.map_err(TransportErrorKind::custom)?; | ||
|
||
// Add the Flashbots signature to the request headers | ||
let headers: HttpHeaderExtension = HttpHeaderExtension::from_iter([( | ||
FLASHBOTS_SIGNATURE_HEADER.to_string(), | ||
signature, | ||
)]); | ||
cakevm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Patch the existing RPC call with the new headers | ||
let rpc_call = self | ||
.inner | ||
.map_meta(|meta| { | ||
let mut meta = meta; | ||
meta.extensions_mut().insert(headers.clone()); | ||
meta | ||
}) | ||
.map_err(TransportErrorKind::custom)?; | ||
|
||
rpc_call.await | ||
}; | ||
return ProviderCall::BoxedFuture(Box::pin(fut)); | ||
} | ||
ProviderCall::RpcCall(self.inner) | ||
} | ||
} | ||
|
||
/// Uses the provided signer to generate a signature for Flashbots authentication. | ||
/// Returns the value for the `X-Flashbots-Signature` header. | ||
/// | ||
/// See [here](https://docs.flashbots.net/flashbots-auction/advanced/rpc-endpoint#authentication) for more information. | ||
pub async fn sign_flashbots_payload<S: Signer + Send + Sync>( | ||
body: String, | ||
signer: &S, | ||
) -> Result<String, alloy_signer::Error> { | ||
let message_hash = keccak256(body.as_bytes()).to_string(); | ||
cakevm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let signature = signer.sign_message(message_hash.as_bytes()).await?; | ||
Ok(format!("{}:{}", signer.address(), hex::encode_prefixed(signature.as_bytes()))) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use alloy_primitives::b256; | ||
use alloy_signer_local::PrivateKeySigner; | ||
|
||
#[tokio::test] | ||
async fn test_sign_flashbots_payload() { | ||
let signer = PrivateKeySigner::from_bytes(&b256!( | ||
"0x0000000000000000000000000000000000000000000000000000000000123456" | ||
)) | ||
.unwrap(); | ||
let body = "sign this message".to_string(); | ||
let signature = sign_flashbots_payload(body.clone(), &signer).await.unwrap(); | ||
assert_eq!(signature, "0xd5F5175D014F28c85F7D67A111C2c9335D7CD771:0x983dc7c520db0d287faff3cd0aef81d5a7f4ffd3473440d3f705da16299724271f660b6fe367f455b205bc014eff3e20defd011f92000f94d39365ca0bc786721b"); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.