reth_rpc_eth_types/id_provider.rs
1//! Helper type for `reth_rpc_eth_api::EthPubSubApiServer` implementation.
2//!
3//! Generates IDs for tracking subscriptions.
4
5use std::fmt::Write;
6
7use jsonrpsee_types::SubscriptionId;
8
9/// An [`IdProvider`](jsonrpsee_core::traits::IdProvider) for ethereum subscription ids.
10///
11/// Returns new hex-string [QUANTITY](https://ethereum.org/en/developers/docs/apis/json-rpc/#quantities-encoding) ids
12#[derive(Debug, Clone, Copy, Default)]
13#[non_exhaustive]
14pub struct EthSubscriptionIdProvider;
15
16impl jsonrpsee_core::traits::IdProvider for EthSubscriptionIdProvider {
17 fn next_id(&self) -> SubscriptionId<'static> {
18 to_quantity(rand::random::<u128>())
19 }
20}
21
22/// Returns a hex quantity string for the given value
23///
24/// Strips all leading zeros, `0` is returned as `0x0`
25#[inline(always)]
26fn to_quantity(val: u128) -> SubscriptionId<'static> {
27 let bytes = val.to_be_bytes();
28 let b = bytes.as_slice();
29 let non_zero = b.iter().take_while(|b| **b == 0).count();
30 let b = &b[non_zero..];
31 if b.is_empty() {
32 return SubscriptionId::Str("0x0".into())
33 }
34
35 let mut id = String::with_capacity(2 * b.len() + 2);
36 id.push_str("0x");
37 let first_byte = b[0];
38 write!(id, "{first_byte:x}").unwrap();
39
40 for byte in &b[1..] {
41 write!(id, "{byte:02x}").unwrap();
42 }
43 id.into()
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use alloy_primitives::U128;
50
51 #[test]
52 fn test_id_provider_quantity() {
53 let id = to_quantity(0);
54 assert_eq!(id, SubscriptionId::Str("0x0".into()));
55 let id = to_quantity(1);
56 assert_eq!(id, SubscriptionId::Str("0x1".into()));
57
58 for _ in 0..1000 {
59 let val = rand::random::<u128>();
60 let id = to_quantity(val);
61 match id {
62 SubscriptionId::Str(id) => {
63 let from_hex: U128 = id.parse().unwrap();
64 assert_eq!(from_hex, U128::from(val));
65 }
66 SubscriptionId::Num(_) => {
67 unreachable!()
68 }
69 }
70 }
71 }
72}