Skip to content

Commit fbc5732

Browse files
committed
add builder client
1 parent 157019c commit fbc5732

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

builder-client/src/lib.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use async_trait::async_trait;
2+
use ethereum_apis_common::{build_response, ErrorResponse};
3+
use reqwest::Client;
4+
use reqwest::Url;
5+
use serde::de::DeserializeOwned;
6+
use types::{
7+
builder_bid::SignedBuilderBid, eth_spec::EthSpec, ExecutionBlockHash, ExecutionPayload,
8+
ForkName, PublicKeyBytes, SignedBlindedBeaconBlock, SignedValidatorRegistrationData, Slot,
9+
};
10+
11+
#[derive(Debug)]
12+
pub enum Error {
13+
Reqwest(reqwest::Error),
14+
InvalidJson(serde_json::Error, String),
15+
ServerMessage(ErrorResponse),
16+
StatusCode(reqwest::StatusCode),
17+
InvalidUrl(Url),
18+
}
19+
20+
impl From<reqwest::Error> for Error {
21+
fn from(e: reqwest::Error) -> Self {
22+
Error::Reqwest(e)
23+
}
24+
}
25+
26+
#[derive(Clone)]
27+
pub struct BuilderClient {
28+
client: Client,
29+
base_url: Url,
30+
}
31+
32+
impl BuilderClient {
33+
pub fn new(base_url: Url) -> Self {
34+
Self {
35+
client: Client::new(),
36+
base_url,
37+
}
38+
}
39+
40+
async fn build_response<T>(&self, response: reqwest::Response) -> Result<T, Error>
41+
where
42+
T: DeserializeOwned,
43+
{
44+
let status = response.status();
45+
let text = response.text().await?;
46+
47+
if status.is_success() {
48+
serde_json::from_str(&text).map_err(|e| Error::InvalidJson(e, text))
49+
} else {
50+
Err(Error::ServerMessage(
51+
serde_json::from_str(&text).map_err(|e| Error::InvalidJson(e, text))?,
52+
))
53+
}
54+
}
55+
56+
pub async fn register_validators(
57+
&self,
58+
registrations: Vec<SignedValidatorRegistrationData>,
59+
) -> Result<(), Error> {
60+
let mut url = self.base_url.clone();
61+
url.path_segments_mut()
62+
.map_err(|_| Error::InvalidUrl(self.base_url.clone()))?
63+
.extend(&["eth", "v1", "builder", "validators"]);
64+
65+
let response = self.client.post(url).json(&registrations).send().await?;
66+
67+
self.build_response(response).await
68+
}
69+
70+
pub async fn submit_blinded_block<E: EthSpec>(
71+
&self,
72+
block: SignedBlindedBeaconBlock<E>,
73+
) -> Result<ExecutionPayload<E>, Error> {
74+
let mut url = self.base_url.clone();
75+
url.path_segments_mut()
76+
.map_err(|_| Error::InvalidUrl(self.base_url.clone()))?
77+
.extend(&["eth", "v1", "builder", "blinded_blocks"]);
78+
79+
let response = self.client.post(url).json(&block).send().await?;
80+
81+
self.build_response(response).await
82+
}
83+
84+
pub async fn get_header<E: EthSpec>(
85+
&self,
86+
slot: Slot,
87+
parent_hash: ExecutionBlockHash,
88+
pubkey: PublicKeyBytes,
89+
) -> Result<SignedBuilderBid<E>, Error> {
90+
let mut url = self.base_url.clone();
91+
url.path_segments_mut()
92+
.map_err(|_| Error::InvalidUrl(self.base_url.clone()))?
93+
.extend(&[
94+
"eth",
95+
"v1",
96+
"builder",
97+
"header",
98+
&slot.to_string(),
99+
&parent_hash.to_string(),
100+
&pubkey.to_string(),
101+
]);
102+
103+
let response = self.client.get(url).send().await?;
104+
105+
self.build_response(response).await
106+
}
107+
108+
pub async fn get_status(&self) -> Result<(), Error> {
109+
let mut url = self.base_url.clone();
110+
url.path_segments_mut()
111+
.map_err(|_| Error::InvalidUrl(self.base_url.clone()))?
112+
.extend(&["eth", "v1", "builder", "status"]);
113+
114+
let response = self.client.get(url).send().await?;
115+
116+
if response.status().is_success() {
117+
Ok(())
118+
} else {
119+
Err(Error::StatusCode(response.status()))
120+
}
121+
}
122+
}
123+

0 commit comments

Comments
 (0)