blueprint_eigenlayer_extra/sidecar/
client.rs1use super::types::*;
2use crate::error::{EigenlayerExtraError, Result};
3use reqwest::Client;
4use std::time::Duration;
5
6#[derive(Clone)]
11pub struct SidecarClient {
12 base_url: String,
13 client: Client,
14}
15
16impl SidecarClient {
17 pub fn new(base_url: String) -> Result<Self> {
22 let client = Client::builder()
23 .timeout(Duration::from_secs(300))
24 .build()
25 .map_err(|e| {
26 EigenlayerExtraError::Other(format!("Failed to create HTTP client: {e}"))
27 })?;
28
29 Ok(Self { base_url, client })
30 }
31
32 pub async fn generate_claim_proof(
39 &self,
40 earner_address: &str,
41 tokens: Vec<String>,
42 root_index: Option<i64>,
43 ) -> Result<Proof> {
44 let url = format!("{}/rewards/v1/claim-proof", self.base_url);
45 let req = GenerateClaimProofRequest {
46 earner_address: earner_address.to_string(),
47 tokens,
48 root_index,
49 };
50
51 let response = self
52 .client
53 .post(&url)
54 .header("x-sidecar-source", "tangle-cli")
55 .json(&req)
56 .send()
57 .await
58 .map_err(|e| EigenlayerExtraError::Other(format!("Sidecar request failed: {e}")))?;
59
60 if !response.status().is_success() {
61 let status = response.status();
62 let body = response.text().await.unwrap_or_default();
63 return Err(EigenlayerExtraError::Other(format!(
64 "Sidecar API error {status}: {body}"
65 )));
66 }
67
68 let resp: GenerateClaimProofResponse = response
69 .json()
70 .await
71 .map_err(|e| EigenlayerExtraError::Other(format!("Failed to parse response: {e}")))?;
72
73 Ok(resp.proof)
74 }
75
76 pub async fn get_summarized_rewards(
84 &self,
85 earner_address: &str,
86 block_height: Option<u64>,
87 ) -> Result<Vec<SummarizedEarnerReward>> {
88 let mut url = format!(
89 "{}/rewards/v1/earners/{}/summarized-rewards",
90 self.base_url, earner_address
91 );
92
93 if let Some(height) = block_height {
94 url.push_str(&format!("?blockHeight={height}"));
95 }
96
97 let response = self
98 .client
99 .get(&url)
100 .header("x-sidecar-source", "tangle-cli")
101 .send()
102 .await
103 .map_err(|e| EigenlayerExtraError::Other(format!("Sidecar request failed: {e}")))?;
104
105 if !response.status().is_success() {
106 let status = response.status();
107 let body = response.text().await.unwrap_or_default();
108 return Err(EigenlayerExtraError::Other(format!(
109 "Sidecar API error {status}: {body}"
110 )));
111 }
112
113 let resp: GetSummarizedRewardsResponse = response
114 .json()
115 .await
116 .map_err(|e| EigenlayerExtraError::Other(format!("Failed to parse response: {e}")))?;
117
118 Ok(resp.rewards)
119 }
120
121 pub async fn list_distribution_roots(
126 &self,
127 block_height: Option<u64>,
128 ) -> Result<Vec<DistributionRoot>> {
129 let mut url = format!("{}/rewards/v1/distribution-roots", self.base_url);
130
131 if let Some(height) = block_height {
132 url.push_str(&format!("?blockHeight={height}"));
133 }
134
135 let response = self
136 .client
137 .get(&url)
138 .header("x-sidecar-source", "tangle-cli")
139 .send()
140 .await
141 .map_err(|e| EigenlayerExtraError::Other(format!("Sidecar request failed: {e}")))?;
142
143 if !response.status().is_success() {
144 let status = response.status();
145 let body = response.text().await.unwrap_or_default();
146 return Err(EigenlayerExtraError::Other(format!(
147 "Sidecar API error {status}: {body}"
148 )));
149 }
150
151 let resp: ListDistributionRootsResponse = response
152 .json()
153 .await
154 .map_err(|e| EigenlayerExtraError::Other(format!("Failed to parse response: {e}")))?;
155
156 Ok(resp.distribution_roots)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[tokio::test]
165 #[ignore = "Requires Sidecar API endpoint"]
166 async fn test_sidecar_client_creation() {
167 let client = SidecarClient::new("https://sidecar-rpc.eigenlayer.xyz/holesky".to_string());
168 assert!(client.is_ok());
169 }
170
171 #[tokio::test]
172 #[ignore = "Requires Sidecar API endpoint and valid earner address"]
173 async fn test_get_summarized_rewards() {
174 let client =
175 SidecarClient::new("https://sidecar-rpc.eigenlayer.xyz/holesky".to_string()).unwrap();
176
177 let result = client
179 .get_summarized_rewards("0x0000000000000000000000000000000000000000", None)
180 .await;
181
182 println!("Result: {result:?}");
184 }
185}