Skip to content

Commit ab81444

Browse files
committed
Use Flashbots bundle type even in Beaver and Titan types
1 parent fbc2f38 commit ab81444

File tree

4 files changed

+57
-111
lines changed

4 files changed

+57
-111
lines changed

searcher-api-types/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ alloy-primitives = "0.8"
1111
alloy-rlp = "0.3"
1212
alloy-rpc-types-mev = "0.8"
1313
eyre = "0.6.12"
14+
serde_with = "3.12.0"

searcher-api-types/src/beaver.rs

Lines changed: 34 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,43 @@
11
//! RPC types that are supported by Beaverbuild
22
use alloy_primitives::{hex::FromHex, Address, BlockNumber, Bytes, TxHash};
3-
use serde::ser::{Serialize, SerializeStruct, Serializer};
3+
use alloy_rpc_types_mev::EthSendBundle;
4+
use serde::Serialize;
5+
use serde_with::{serde_as, skip_serializing_none};
46

57
/// Bundle as recognised by Beaverbuild
68
///
79
/// Consult <https://beaverbuild.org/docs.html>. Note that the deprecated `replacementUuid` field
810
/// has been omitted.
9-
#[derive(Clone, Debug, Default)]
11+
#[serde_as]
12+
#[skip_serializing_none]
13+
#[derive(Clone, Debug, Default, Serialize)]
1014
pub struct BeaverBundle {
11-
/// List of hex-encoded, raw transactions. Can be empty for cancelling a bundle
12-
pub transactions: Vec<Bytes>,
13-
/// The block that this bundle will be valid for. 0 means it's valid for the next block (and only this one)
14-
pub block_number: BlockNumber,
15-
/// If specified and >0, the bundle will only be valid if the block timestamp is greater or equal to `minTimestamp`
16-
pub min_timestamp: Option<u64>,
17-
/// If specified and >0, the bundle will only be valid if the block timestamp is smaller or equal to `maxTimestamp`
18-
pub max_timestamp: Option<u64>,
19-
/// A list of transaction hashes contained in the bundle, that can be allowed to revert, or be removed from your bundle if it's deemed useful
20-
pub reverting_transaction_hashes: Vec<TxHash>,
15+
#[serde(flatten)]
16+
pub bundle: EthSendBundle,
17+
#[serde(skip_serializing_if = "Vec::is_empty")]
2118
/// A list of transaction hashes contained in the bundle, that can be allowed to be removed from your bundle if it's deemed useful (but not revert)
2219
pub dropping_transaction_hashes: Vec<TxHash>,
23-
/// An UUID string, which allows you to update/cancel your bundles: if you specify an uuid and we already have a bundle with an identical one, we'll forget about the old bundle. So we can only have a single bundle with a certain `uuid` at all times (and we keep the most recent)
24-
pub uuid: Option<String>,
2520
/// An integer between 1-99. How much of the total priority fee + coinbase payment you want to be refunded for. This will negatively impact your prioritization because this refund is gonna eat into your bundle payment. Example: if a bundle pays 0.2 ETH of priority fee plus 1 ETH to coinbase, a refundPercent set to 50 will result in a transaction being appended after the bundle, paying 0.59 ETH back to the EOA. This is assuming the payout tx will cost beaver 0.01 ETH in fees, which are deduced from the 0.6 ETH payout.
2621
pub refund_percent: Option<u64>,
2722
/// You can specify an address that the funds from `refundPercent` will be sent to. If not specified, they will be sent to the `from` address of the first transaction
2823
pub refund_recipient: Option<Address>,
24+
#[serde(skip_serializing_if = "Vec::is_empty")]
2925
/// The hashes of transactions in the bundle that the refund will be based on. If it's empty, we'll use the last transaction
3026
pub refund_transaction_hashes: Vec<TxHash>,
3127
}
3228

33-
impl BeaverBundle {
34-
pub fn from_rlp_hex(txs: Vec<String>, block_number: BlockNumber) -> eyre::Result<Self> {
35-
Ok(Self {
36-
transactions: txs
37-
.iter()
38-
.map(Bytes::from_hex)
39-
.collect::<Result<Vec<Bytes>, _>>()?,
40-
block_number,
41-
..Self::default()
42-
})
43-
}
44-
}
45-
46-
impl Serialize for BeaverBundle {
47-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
48-
where
49-
S: Serializer,
50-
{
51-
let mut state = serializer.serialize_struct("BeaverBundle", 2)?;
52-
state.serialize_field("txs", &self.transactions)?;
53-
state.serialize_field("blockNumber", &format!("0x{:x}", self.block_number))?;
54-
55-
if let Some(ref t) = self.min_timestamp {
56-
state.serialize_field("minTimestamp", t)?;
57-
}
58-
59-
if let Some(ref t) = self.max_timestamp {
60-
state.serialize_field("maxTimestamp", t)?;
61-
}
62-
63-
if !self.reverting_transaction_hashes.is_empty() {
64-
state.serialize_field(
65-
"revertingTxHashes",
66-
&self
67-
.reverting_transaction_hashes
68-
.iter()
69-
.map(|hash| hash.to_string())
70-
.collect::<Vec<String>>(),
71-
)?;
72-
}
73-
74-
if !self.dropping_transaction_hashes.is_empty() {
75-
state.serialize_field(
76-
"droppingTxHashes",
77-
&self
78-
.dropping_transaction_hashes
79-
.iter()
80-
.map(|hash| hash.to_string())
81-
.collect::<Vec<String>>(),
82-
)?;
83-
}
84-
85-
if let Some(ref uuid) = self.uuid {
86-
state.serialize_field("uuid", uuid)?;
87-
}
88-
89-
if let Some(ref refund_pct) = self.refund_percent {
90-
state.serialize_field("refundPercent", refund_pct)?;
91-
}
92-
93-
if let Some(ref refund_addr) = self.refund_recipient {
94-
state.serialize_field("refundRecipient", refund_addr)?;
95-
}
96-
97-
if !self.refund_transaction_hashes.is_empty() {
98-
state.serialize_field(
99-
"refundTxHashes",
100-
&self
101-
.refund_transaction_hashes
102-
.iter()
103-
.map(|hash| hash.to_string())
104-
.collect::<Vec<String>>(),
105-
)?;
106-
}
107-
108-
state.end()
109-
}
29+
pub fn bundle_from_rlp_hex(
30+
txs: Vec<String>,
31+
block_number: BlockNumber,
32+
) -> eyre::Result<EthSendBundle> {
33+
Ok(EthSendBundle {
34+
txs: txs
35+
.iter()
36+
.map(Bytes::from_hex)
37+
.collect::<Result<Vec<Bytes>, _>>()?,
38+
block_number,
39+
..EthSendBundle::default()
40+
})
11041
}
11142

11243
#[cfg(test)]
@@ -122,28 +53,34 @@ mod test {
12253
);
12354

12455
assert!(serde_json::to_string(&BeaverBundle {
125-
transactions: vec![],
126-
block_number: 21862873,
56+
bundle: EthSendBundle {
57+
txs: vec![],
58+
block_number: 21862873,
59+
..Default::default()
60+
},
12761
..Default::default()
12862
})
12963
.is_ok());
13064
assert_eq!(
13165
serde_json::to_string(&BeaverBundle {
132-
transactions: vec![],
133-
block_number: 21862873,
66+
bundle: EthSendBundle {
67+
txs: vec![],
68+
block_number: 21862873,
69+
..Default::default()
70+
},
13471
..Default::default()
13572
})
13673
.unwrap(),
13774
"{\"txs\":[],\"blockNumber\":\"0x14d99d9\"}".to_string()
13875
);
13976
assert!(
14077
serde_json::to_string(&
141-
BeaverBundle::from_rlp_hex(vec!["0x02f8b20181948449bdee618501dcd6500083016b93942dabcea55a12d73191aece59f508b191fb68adac80b844095ea7b300000000000000000000000054e44dbb92dba848ace27f44c0cb4268981ef1cc00000000000000000000000000000000000000000000000052616e065f6915ebc080a0c497b6e53d7cb78e68c37f6186c8bb9e1b8a55c3e22462163495979b25c2caafa052769811779f438b73159c4cc6a05a889da8c1a16e432c2e37e3415c9a0b9887".to_string()], 21862873).unwrap()
78+
bundle_from_rlp_hex(vec!["0x02f8b20181948449bdee618501dcd6500083016b93942dabcea55a12d73191aece59f508b191fb68adac80b844095ea7b300000000000000000000000054e44dbb92dba848ace27f44c0cb4268981ef1cc00000000000000000000000000000000000000000000000052616e065f6915ebc080a0c497b6e53d7cb78e68c37f6186c8bb9e1b8a55c3e22462163495979b25c2caafa052769811779f438b73159c4cc6a05a889da8c1a16e432c2e37e3415c9a0b9887".to_string()], 21862873).unwrap()
14279
)
14380
.is_ok());
14481
assert_eq!(
14582
serde_json::to_string(&
146-
BeaverBundle::from_rlp_hex(vec!["0x02f8b20181948449bdee618501dcd6500083016b93942dabcea55a12d73191aece59f508b191fb68adac80b844095ea7b300000000000000000000000054e44dbb92dba848ace27f44c0cb4268981ef1cc00000000000000000000000000000000000000000000000052616e065f6915ebc080a0c497b6e53d7cb78e68c37f6186c8bb9e1b8a55c3e22462163495979b25c2caafa052769811779f438b73159c4cc6a05a889da8c1a16e432c2e37e3415c9a0b9887".to_string()], 21862873).unwrap()
83+
bundle_from_rlp_hex(vec!["0x02f8b20181948449bdee618501dcd6500083016b93942dabcea55a12d73191aece59f508b191fb68adac80b844095ea7b300000000000000000000000054e44dbb92dba848ace27f44c0cb4268981ef1cc00000000000000000000000000000000000000000000000052616e065f6915ebc080a0c497b6e53d7cb78e68c37f6186c8bb9e1b8a55c3e22462163495979b25c2caafa052769811779f438b73159c4cc6a05a889da8c1a16e432c2e37e3415c9a0b9887".to_string()], 21862873).unwrap()
14784
)
14885
.unwrap(),
14986
"{\"txs\":[\"0x02f8b20181948449bdee618501dcd6500083016b93942dabcea55a12d73191aece59f508b191fb68adac80b844095ea7b300000000000000000000000054e44dbb92dba848ace27f44c0cb4268981ef1cc00000000000000000000000000000000000000000000000052616e065f6915ebc080a0c497b6e53d7cb78e68c37f6186c8bb9e1b8a55c3e22462163495979b25c2caafa052769811779f438b73159c4cc6a05a889da8c1a16e432c2e37e3415c9a0b9887\"],\"blockNumber\":\"0x14d99d9\"}".to_string()

searcher-api-types/src/lib.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,39 @@ impl SendBundleRequest {
2828
pub fn min_timestamp(&self) -> Option<u64> {
2929
match self {
3030
SendBundleRequest::Flashbots(bundle) => bundle.min_timestamp,
31-
SendBundleRequest::Beaver(bundle) => bundle.min_timestamp,
32-
SendBundleRequest::Titan(bundle) => bundle.min_timestamp,
31+
SendBundleRequest::Beaver(bundle) => bundle.bundle.min_timestamp,
32+
SendBundleRequest::Titan(bundle) => bundle.bundle.min_timestamp,
3333
}
3434
}
3535

3636
pub fn max_timestamp(&self) -> Option<u64> {
3737
match self {
3838
SendBundleRequest::Flashbots(bundle) => bundle.max_timestamp,
39-
SendBundleRequest::Beaver(bundle) => bundle.max_timestamp,
40-
SendBundleRequest::Titan(bundle) => bundle.max_timestamp,
39+
SendBundleRequest::Beaver(bundle) => bundle.bundle.max_timestamp,
40+
SendBundleRequest::Titan(bundle) => bundle.bundle.max_timestamp,
4141
}
4242
}
4343

4444
pub fn block_number(&self) -> BlockNumber {
4545
match self {
4646
SendBundleRequest::Flashbots(bundle) => bundle.block_number,
47-
SendBundleRequest::Beaver(bundle) => bundle.block_number,
48-
SendBundleRequest::Titan(bundle) => bundle.block_number,
47+
SendBundleRequest::Beaver(bundle) => bundle.bundle.block_number,
48+
SendBundleRequest::Titan(bundle) => bundle.bundle.block_number,
4949
}
5050
}
5151

5252
pub fn tx_bytes(&self) -> Vec<Bytes> {
5353
match self {
5454
SendBundleRequest::Flashbots(bundle) => bundle.txs.clone(),
5555
SendBundleRequest::Beaver(bundle) => bundle
56-
.transactions
56+
.bundle
57+
.txs
5758
.iter()
5859
.map(|tx| encode(tx).into())
5960
.collect(),
6061
SendBundleRequest::Titan(bundle) => bundle
61-
.transactions
62+
.bundle
63+
.txs
6264
.iter()
6365
.map(|tx| encode(tx).into())
6466
.collect(),
@@ -68,8 +70,8 @@ impl SendBundleRequest {
6870
pub fn reverting_tx_hashes(&self) -> Vec<TxHash> {
6971
match self {
7072
SendBundleRequest::Flashbots(_) => vec![],
71-
SendBundleRequest::Beaver(bundle) => bundle.reverting_transaction_hashes.clone(),
72-
SendBundleRequest::Titan(bundle) => bundle.reverting_transaction_hashes.clone(),
73+
SendBundleRequest::Beaver(bundle) => bundle.bundle.reverting_tx_hashes.clone(),
74+
SendBundleRequest::Titan(bundle) => bundle.bundle.reverting_tx_hashes.clone(),
7375
}
7476
}
7577
}

searcher-client/src/lib.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ pub async fn send_bundle(url: Url, bundle: &SendBundleRequest) -> eyre::Result<E
1717
mod test {
1818
use super::*;
1919

20-
use searcher_api_types::{BeaverBundle, SendBundleRequest};
20+
use alloy_rpc_types_mev::EthSendBundle;
21+
use searcher_api_types::{bundle_from_rlp_hex, BeaverBundle, SendBundleRequest};
2122

2223
const TEST_ENDPOINT: &str = "https://rpc.beaverbuild.org";
2324

@@ -28,8 +29,11 @@ mod test {
2829
#[tokio::test]
2930
async fn test_send_bundle_beaver_rejects_empty_bundle() {
3031
let empty_bundle = SendBundleRequest::Beaver(BeaverBundle {
31-
transactions: vec![],
32-
block_number: 0,
32+
bundle: EthSendBundle {
33+
txs: vec![],
34+
block_number: 0,
35+
..EthSendBundle::default()
36+
},
3337
..BeaverBundle::default()
3438
});
3539
let res = send_bundle(test_endpoint(), &empty_bundle).await;
@@ -38,7 +42,9 @@ mod test {
3842

3943
#[tokio::test]
4044
async fn test_send_bundle_beaver_success() {
41-
let bundle = SendBundleRequest::Beaver(BeaverBundle::from_rlp_hex(vec!["0x02f8b20181948449bdee618501dcd6500083016b93942dabcea55a12d73191aece59f508b191fb68adac80b844095ea7b300000000000000000000000054e44dbb92dba848ace27f44c0cb4268981ef1cc00000000000000000000000000000000000000000000000052616e065f6915ebc080a0c497b6e53d7cb78e68c37f6186c8bb9e1b8a55c3e22462163495979b25c2caafa052769811779f438b73159c4cc6a05a889da8c1a16e432c2e37e3415c9a0b9887".to_string()], 1).expect("illegal RLP bytes for bundle"));
45+
let bundle = SendBundleRequest::Beaver(
46+
BeaverBundle { bundle: bundle_from_rlp_hex(vec!["0x02f8b20181948449bdee618501dcd6500083016b93942dabcea55a12d73191aece59f508b191fb68adac80b844095ea7b300000000000000000000000054e44dbb92dba848ace27f44c0cb4268981ef1cc00000000000000000000000000000000000000000000000052616e065f6915ebc080a0c497b6e53d7cb78e68c37f6186c8bb9e1b8a55c3e22462163495979b25c2caafa052769811779f438b73159c4cc6a05a889da8c1a16e432c2e37e3415c9a0b9887".to_string()], 1).expect("illegal RLP bytes for bundle"), ..BeaverBundle::default()}
47+
);
4248
let res = send_bundle(test_endpoint(), &bundle).await;
4349
assert!(res.is_ok());
4450
let resp = res.unwrap();

0 commit comments

Comments
 (0)