1use std::fmt;
9
10use crate::client::evm::abi::{self, Token};
11use crate::client::evm::records::{Block, FeeHistory, Log, Receipt, Transaction};
12use crate::client::evm::tx::Call;
13use crate::client::evm::u256::{B256, U256};
14use crate::client::{Http, Network, Request, TransportError};
15use crate::utils::{Address, Json, hex};
16use serde::{Deserialize, Deserializer, de::DeserializeOwned};
17use serde_json::value::RawValue;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub enum BlockTag {
22 #[default]
23 Latest,
24 Pending,
26 Earliest,
27 Safe,
28 Finalized,
29 Number(u64),
30}
31
32impl fmt::Display for BlockTag {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 match self {
35 BlockTag::Latest => f.write_str("latest"),
36 BlockTag::Pending => f.write_str("pending"),
37 BlockTag::Earliest => f.write_str("earliest"),
38 BlockTag::Safe => f.write_str("safe"),
39 BlockTag::Finalized => f.write_str("finalized"),
40 BlockTag::Number(number) => write!(f, "{number:#x}"),
41 }
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Default)]
48pub struct CallRequest {
49 pub from: Option<Address>,
50 pub to: Option<Address>,
51 pub gas: Option<u64>,
52 pub value: Option<U256>,
53 pub data: Vec<u8>,
54}
55
56impl CallRequest {
57 #[must_use]
59 pub fn to(to: Address, data: Vec<u8>) -> CallRequest {
60 CallRequest { to: Some(to), data, ..CallRequest::default() }
61 }
62
63 #[must_use]
65 pub fn of(from: Option<Address>, call: &Call) -> CallRequest {
66 CallRequest {
67 from,
68 to: call.to,
69 gas: None,
70 value: (!call.value.is_zero()).then_some(call.value),
71 data: call.data.clone(),
72 }
73 }
74
75 fn json(&self) -> Json {
76 let mut fields = Vec::new();
77 if let Some(from) = self.from {
78 fields.push(("from".to_owned(), Json::String(from.to_hex())));
79 }
80 if let Some(to) = self.to {
81 fields.push(("to".to_owned(), Json::String(to.to_hex())));
82 }
83 if let Some(gas) = self.gas {
84 fields.push(("gas".to_owned(), quantity(U256::from_u64(gas))));
85 }
86 if let Some(value) = self.value {
87 fields.push(("value".to_owned(), quantity(value)));
88 }
89 if !self.data.is_empty() {
90 fields.push(("data".to_owned(), Json::String(hex::encode_prefixed(&self.data))));
91 }
92 Json::Object(fields)
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Default)]
100pub struct LogFilter {
101 pub from_block: Option<BlockTag>,
102 pub to_block: Option<BlockTag>,
103 pub block_hash: Option<B256>,
105 pub address: Vec<Address>,
106 pub topics: Vec<Option<Vec<B256>>>,
107}
108
109impl LogFilter {
110 fn json(&self) -> Json {
111 let mut fields = Vec::new();
112 if let Some(from) = self.from_block {
113 fields.push(("fromBlock".to_owned(), Json::String(from.to_string())));
114 }
115 if let Some(to) = self.to_block {
116 fields.push(("toBlock".to_owned(), Json::String(to.to_string())));
117 }
118 if let Some(hash) = self.block_hash {
119 fields.push(("blockHash".to_owned(), Json::String(hash.to_hex())));
120 }
121 match self.address.as_slice() {
122 [] => {}
123 [one] => fields.push(("address".to_owned(), Json::String(one.to_hex()))),
124 many => fields.push((
125 "address".to_owned(),
126 Json::Array(many.iter().map(|address| Json::String(address.to_hex())).collect()),
127 )),
128 }
129 if !self.topics.is_empty() {
130 let topics = self
131 .topics
132 .iter()
133 .map(|place| match place.as_deref() {
134 None => Json::Null,
135 Some([one]) => Json::String(one.to_hex()),
136 Some(many) => Json::Array(many.iter().map(|topic| Json::String(topic.to_hex())).collect()),
137 })
138 .collect();
139 fields.push(("topics".to_owned(), Json::Array(topics)));
140 }
141 Json::Object(fields)
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
147pub struct NodeError {
148 pub code: i64,
149 pub message: String,
150 pub data: Option<String>,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum ErrorKind {
157 Parse,
159 InvalidRequest,
161 MethodNotFound,
163 InvalidParams,
165 Internal,
167 Reverted,
169 Server,
171 Other,
172}
173
174impl NodeError {
175 #[must_use]
176 pub fn kind(&self) -> ErrorKind {
177 match self.code {
178 -32700 => ErrorKind::Parse,
179 -32600 => ErrorKind::InvalidRequest,
180 -32601 => ErrorKind::MethodNotFound,
181 -32602 => ErrorKind::InvalidParams,
182 -32603 => ErrorKind::Internal,
183 3 => ErrorKind::Reverted,
184 -32099..=-32000 if self.message.starts_with("execution reverted") => ErrorKind::Reverted,
185 -32099..=-32000 => ErrorKind::Server,
186 _ => ErrorKind::Other,
187 }
188 }
189
190 #[must_use]
192 pub fn revert(&self) -> Option<Revert> {
193 let data = self.data.as_deref()?.strip_prefix("0x")?;
194 hex::decode(data).ok().map(|bytes| read_revert(&bytes))
195 }
196}
197
198impl fmt::Display for NodeError {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 write!(f, "{} (code {})", self.message, self.code)?;
201 match &self.data {
202 Some(data) => write!(f, ": {data}"),
203 None => Ok(()),
204 }
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
210pub enum Revert {
211 Message(String),
213 Panic(U256),
215 Custom(Vec<u8>),
217}
218
219const ERROR: [u8; 4] = [0x08, 0xc3, 0x79, 0xa0];
221const PANIC: [u8; 4] = [0x4e, 0x48, 0x7b, 0x71];
223
224#[must_use]
226pub fn read_revert(data: &[u8]) -> Revert {
227 let custom = || Revert::Custom(data.to_vec());
228 let Some((selector, rest)) = data.split_first_chunk::<4>() else {
229 return custom();
230 };
231 let shape = match *selector {
232 ERROR => "string",
233 PANIC => "uint256",
234 _ => return custom(),
235 };
236 let decoded = abi::params(shape).and_then(|params| abi::decode(¶ms, rest));
237 match decoded.as_deref() {
238 Ok([Token::String(text)]) => Revert::Message(text.clone()),
239 Ok([Token::Uint(code)]) => Revert::Panic(*code),
240 _ => custom(),
241 }
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
245pub enum RpcError {
246 #[error(transparent)]
248 Transport(#[from] TransportError),
249 #[error("{method}: the node refused: {error}")]
250 Node { method: &'static str, error: NodeError },
251 #[error("{method}: the node's answer could not be read: {detail}")]
252 Unreadable { method: &'static str, detail: String },
253}
254
255#[derive(Debug, Clone)]
257pub struct Query<T> {
258 method: &'static str,
259 params: Vec<Json>,
260 read: fn(&str) -> Result<T, String>,
261}
262
263impl<T> Query<T> {
264 #[must_use]
265 pub const fn method(&self) -> &'static str {
266 self.method
267 }
268
269 #[must_use]
271 pub fn body(&self) -> String {
272 Json::Object(vec![
273 ("jsonrpc".to_owned(), Json::String("2.0".to_owned())),
274 ("id".to_owned(), Json::Number("1".to_owned())),
275 ("method".to_owned(), Json::String(self.method.to_owned())),
276 ("params".to_owned(), Json::Array(self.params.clone())),
277 ])
278 .compact()
279 }
280
281 pub fn answer(&self, text: &str) -> Result<T, RpcError> {
283 let unreadable = |detail: String| RpcError::Unreadable { method: self.method, detail };
284 let envelope: Envelope<'_> = serde_json::from_str(text)
285 .map_err(|error| unreadable(format!("{error} in {}", text.chars().take(300).collect::<String>())))?;
286 if let Some(error) = envelope.error {
287 let data = error
288 .data
289 .map(|data| serde_json::from_str::<String>(data.get()).unwrap_or_else(|_| data.get().to_owned()));
290 return Err(RpcError::Node {
291 method: self.method,
292 error: NodeError { code: error.code, message: error.message, data },
293 });
294 }
295 let result = envelope.result.ok_or_else(|| unreadable("neither a result nor an error".to_owned()))?;
296 (self.read)(result.get()).map_err(|detail| unreadable(format!("{detail} in {}", result.get())))
297 }
298}
299
300#[derive(Deserialize)]
301struct Envelope<'a> {
302 #[serde(default, borrow, deserialize_with = "raw")]
303 result: Option<&'a RawValue>,
304 #[serde(default, borrow)]
305 error: Option<ErrorBody<'a>>,
306}
307
308#[derive(Deserialize)]
309struct ErrorBody<'a> {
310 code: i64,
311 #[serde(default)]
312 message: String,
313 #[serde(default, borrow, deserialize_with = "raw")]
314 data: Option<&'a RawValue>,
315}
316
317fn raw<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<&'de RawValue>, D::Error> {
319 <&RawValue>::deserialize(deserializer).map(Some)
320}
321
322fn json<T: DeserializeOwned>(text: &str) -> Result<T, String> {
323 serde_json::from_str(text).map_err(|error| error.to_string())
324}
325
326fn small(text: &str) -> Result<u64, String> {
327 let value: U256 = json(text)?;
328 value.to_u64().ok_or_else(|| format!("{value} does not fit 64 bits"))
329}
330
331fn data(text: &str) -> Result<Vec<u8>, String> {
332 let said: String = json(text)?;
333 let digits = said.strip_prefix("0x").ok_or_else(|| format!("`{said}` is not 0x and hex"))?;
334 hex::decode(digits).map_err(|error| error.to_string())
335}
336
337fn quantity(value: U256) -> Json {
338 Json::String(value.to_quantity())
339}
340
341fn tag(block: BlockTag) -> Json {
342 Json::String(block.to_string())
343}
344
345fn address(address: Address) -> Json {
346 Json::String(address.to_hex())
347}
348
349pub mod eth {
351 use super::*;
352
353 fn query<T>(method: &'static str, params: Vec<Json>, read: fn(&str) -> Result<T, String>) -> Query<T> {
354 Query { method, params, read }
355 }
356
357 #[must_use]
359 pub fn chain_id() -> Query<u64> {
360 query("eth_chainId", Vec::new(), small)
361 }
362
363 #[must_use]
365 pub fn block_number() -> Query<u64> {
366 query("eth_blockNumber", Vec::new(), small)
367 }
368
369 #[must_use]
371 pub fn balance(owner: Address, block: BlockTag) -> Query<U256> {
372 query("eth_getBalance", vec![address(owner), tag(block)], json)
373 }
374
375 #[must_use]
377 pub fn transaction_count(owner: Address, block: BlockTag) -> Query<u64> {
378 query("eth_getTransactionCount", vec![address(owner), tag(block)], small)
379 }
380
381 #[must_use]
383 pub fn gas_price() -> Query<U256> {
384 query("eth_gasPrice", Vec::new(), json)
385 }
386
387 #[must_use]
389 pub fn max_priority_fee() -> Query<U256> {
390 query("eth_maxPriorityFeePerGas", Vec::new(), json)
391 }
392
393 #[must_use]
396 pub fn fee_history(blocks: u64, newest: BlockTag, percentiles: &[u8]) -> Query<FeeHistory> {
397 let percentiles = percentiles.iter().map(|percent| Json::Number(percent.to_string())).collect();
398 query("eth_feeHistory", vec![quantity(U256::from_u64(blocks)), tag(newest), Json::Array(percentiles)], json)
399 }
400
401 #[must_use]
403 pub fn estimate_gas(call: &CallRequest) -> Query<u64> {
404 query("eth_estimateGas", vec![call.json()], small)
405 }
406
407 #[must_use]
409 pub fn call(call: &CallRequest, block: BlockTag) -> Query<Vec<u8>> {
410 query("eth_call", vec![call.json(), tag(block)], data)
411 }
412
413 #[must_use]
415 pub fn send_raw_transaction(raw: &[u8]) -> Query<B256> {
416 query("eth_sendRawTransaction", vec![Json::String(hex::encode_prefixed(raw))], json)
417 }
418
419 #[must_use]
421 pub fn receipt(hash: B256) -> Query<Option<Receipt>> {
422 query("eth_getTransactionReceipt", vec![Json::String(hash.to_hex())], json)
423 }
424
425 #[must_use]
427 pub fn transaction(hash: B256) -> Query<Option<Transaction>> {
428 query("eth_getTransactionByHash", vec![Json::String(hash.to_hex())], json)
429 }
430
431 #[must_use]
433 pub fn block(block: BlockTag, full: bool) -> Query<Option<Block>> {
434 query("eth_getBlockByNumber", vec![tag(block), Json::Bool(full)], json)
435 }
436
437 #[must_use]
439 pub fn code(at: Address, block: BlockTag) -> Query<Vec<u8>> {
440 query("eth_getCode", vec![address(at), tag(block)], data)
441 }
442
443 #[must_use]
445 pub fn storage_at(at: Address, slot: U256, block: BlockTag) -> Query<B256> {
446 query("eth_getStorageAt", vec![address(at), quantity(slot), tag(block)], json)
447 }
448
449 #[must_use]
451 pub fn logs(filter: &LogFilter) -> Query<Vec<Log>> {
452 query("eth_getLogs", vec![filter.json()], json)
453 }
454
455 #[must_use]
457 pub fn big_block_gas_price() -> Query<U256> {
458 query("eth_bigBlockGasPrice", Vec::new(), json)
459 }
460
461 #[must_use]
464 pub fn using_big_blocks(owner: Address) -> Query<bool> {
465 query("eth_usingBigBlocks", vec![address(owner)], json)
466 }
467}
468
469#[derive(Debug, Clone)]
471pub struct Rpc<H> {
472 http: H,
473 url: String,
474}
475
476impl<H: Http> Rpc<H> {
477 pub fn new(http: H, url: impl Into<String>) -> Self {
479 Rpc { http, url: url.into() }
480 }
481
482 pub fn hyperevm(http: H, network: Network) -> Self {
484 Rpc::new(http, network.evm_rpc_url())
485 }
486
487 #[must_use]
488 pub fn url(&self) -> &str {
489 &self.url
490 }
491
492 #[must_use]
493 pub const fn http(&self) -> &H {
494 &self.http
495 }
496
497 pub async fn run<T>(&self, query: Query<T>) -> Result<T, RpcError> {
499 let response = self.http.send(Request::post_json(self.url.clone(), query.body())).await?;
500 if response.status == 429 {
501 let seconds = response.header("retry-after").and_then(|seconds| seconds.trim().parse::<u64>().ok());
502 return Err(TransportError::RateLimited {
503 retry_after_ms: seconds.map(|seconds| seconds.saturating_mul(1_000)),
504 }
505 .into());
506 }
507 let text = response.text();
508 match (response.is_success(), query.answer(&text)) {
509 (_, Err(refused @ RpcError::Node { .. })) => Err(refused),
510 (true, answer) => answer,
511 (false, _) => Err(TransportError::Status { status: response.status, body: text }.into()),
512 }
513 }
514
515 pub async fn chain_id(&self) -> Result<u64, RpcError> {
516 self.run(eth::chain_id()).await
517 }
518
519 pub async fn block_number(&self) -> Result<u64, RpcError> {
520 self.run(eth::block_number()).await
521 }
522
523 pub async fn balance(&self, owner: Address, block: BlockTag) -> Result<U256, RpcError> {
524 self.run(eth::balance(owner, block)).await
525 }
526
527 pub async fn transaction_count(&self, owner: Address, block: BlockTag) -> Result<u64, RpcError> {
528 self.run(eth::transaction_count(owner, block)).await
529 }
530
531 pub async fn gas_price(&self) -> Result<U256, RpcError> {
532 self.run(eth::gas_price()).await
533 }
534
535 pub async fn max_priority_fee(&self) -> Result<U256, RpcError> {
536 self.run(eth::max_priority_fee()).await
537 }
538
539 pub async fn fee_history(&self, blocks: u64, newest: BlockTag, percentiles: &[u8]) -> Result<FeeHistory, RpcError> {
540 self.run(eth::fee_history(blocks, newest, percentiles)).await
541 }
542
543 pub async fn estimate_gas(&self, call: &CallRequest) -> Result<u64, RpcError> {
544 self.run(eth::estimate_gas(call)).await
545 }
546
547 pub async fn call(&self, call: &CallRequest, block: BlockTag) -> Result<Vec<u8>, RpcError> {
548 self.run(eth::call(call, block)).await
549 }
550
551 pub async fn send_raw_transaction(&self, raw: &[u8]) -> Result<B256, RpcError> {
554 self.run(eth::send_raw_transaction(raw)).await
555 }
556
557 pub async fn receipt(&self, hash: B256) -> Result<Option<Receipt>, RpcError> {
558 self.run(eth::receipt(hash)).await
559 }
560
561 pub async fn transaction(&self, hash: B256) -> Result<Option<Transaction>, RpcError> {
562 self.run(eth::transaction(hash)).await
563 }
564
565 pub async fn block(&self, block: BlockTag, full: bool) -> Result<Option<Block>, RpcError> {
566 self.run(eth::block(block, full)).await
567 }
568
569 pub async fn code(&self, at: Address, block: BlockTag) -> Result<Vec<u8>, RpcError> {
570 self.run(eth::code(at, block)).await
571 }
572
573 pub async fn storage_at(&self, at: Address, slot: U256, block: BlockTag) -> Result<B256, RpcError> {
574 self.run(eth::storage_at(at, slot, block)).await
575 }
576
577 pub async fn logs(&self, filter: &LogFilter) -> Result<Vec<Log>, RpcError> {
578 self.run(eth::logs(filter)).await
579 }
580
581 pub async fn big_block_gas_price(&self) -> Result<U256, RpcError> {
582 self.run(eth::big_block_gas_price()).await
583 }
584
585 pub async fn using_big_blocks(&self, owner: Address) -> Result<bool, RpcError> {
586 self.run(eth::using_big_blocks(owner)).await
587 }
588}
589
590#[cfg(test)]
591#[path = "tests/rpc.rs"]
592mod tests;