fc_rpc_v2_types/
txpool.rs1use std::{collections::BTreeMap, fmt};
20
21use ethereum_types::{Address, U256, U64};
22use serde::{de, Deserialize, Serialize};
23
24use crate::transaction::Transaction;
25
26pub type TxpoolInspect = TxpoolResult<AddressMapping<NonceMapping<Summary>>>;
27pub type TxpoolContent = TxpoolResult<AddressMapping<NonceMapping<Transaction>>>;
28pub type TxpoolContentFrom = TxpoolResult<NonceMapping<Transaction>>;
29pub type TxpoolStatus = TxpoolResult<U64>;
30
31pub type NonceMapping<T> = BTreeMap<u64, T>;
32pub type AddressMapping<T> = BTreeMap<Address, T>;
33
34#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
36pub struct TxpoolResult<T> {
37 pub pending: T,
39 pub queued: T,
41}
42
43#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct Summary {
46 pub to: Option<Address>,
48 pub value: U256,
50 pub gas: u128,
52 pub gas_price: u128,
54}
55
56impl serde::Serialize for Summary {
57 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58 where
59 S: serde::Serializer,
60 {
61 let formatted_to = if let Some(to) = self.to {
62 format!("{to:?}")
63 } else {
64 "contract creation".to_string()
65 };
66 let formatted = format!(
67 "{}: {} wei + {} gas × {} wei",
68 formatted_to, self.value, self.gas, self.gas_price
69 );
70 serializer.serialize_str(&formatted)
71 }
72}
73
74impl<'de> serde::Deserialize<'de> for Summary {
75 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76 where
77 D: serde::Deserializer<'de>,
78 {
79 struct SummaryVisitor;
80 impl de::Visitor<'_> for SummaryVisitor {
81 type Value = Summary;
82
83 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
84 formatter.write_str("{{to}}: {{value}} wei + {{gas}} gas × {{gas_price}} wei")
85 }
86
87 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
88 where
89 E: de::Error,
90 {
91 let addr_split: Vec<&str> = v.split(": ").collect();
92 if addr_split.len() != 2 {
93 return Err(de::Error::custom("invalid `to` format"));
94 }
95
96 let value_split: Vec<&str> = addr_split[1].split(" wei + ").collect();
97 if value_split.len() != 2 {
98 return Err(de::Error::custom("invalid `value` format"));
99 }
100
101 let gas_split: Vec<&str> = value_split[1].split(" gas × ").collect();
102 if gas_split.len() != 2 {
103 return Err(de::Error::custom("invalid `gas` format"));
104 }
105
106 let gas_price_split: Vec<&str> = gas_split[1].split(" wei").collect();
107 if gas_price_split.len() != 2 {
108 return Err(de::Error::custom("invalid `gas_price` format"));
109 }
110
111 let to = match addr_split[0] {
112 "contract creation" => None,
113 addr => {
114 let addr = addr
115 .trim_start_matches("0x")
116 .parse::<Address>()
117 .map_err(de::Error::custom)?;
118 Some(addr)
119 }
120 };
121 let value = U256::from_dec_str(value_split[0]).map_err(de::Error::custom)?;
122 let gas = gas_split[0].parse::<u128>().map_err(de::Error::custom)?;
123 let gas_price = gas_price_split[0]
124 .parse::<u128>()
125 .map_err(de::Error::custom)?;
126
127 Ok(Summary {
128 to,
129 value,
130 gas,
131 gas_price,
132 })
133 }
134
135 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
136 where
137 E: de::Error,
138 {
139 self.visit_str(&v)
140 }
141 }
142
143 deserializer.deserialize_str(SummaryVisitor)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn inspect_summary_serde_impl() {
153 let valid_cases = [
154 (
155 r#""contract creation: 2472666000 wei + 21000 gas × 1000 wei""#,
156 Summary {
157 to: None,
158 value: U256::from(2472666000u64),
159 gas: 21000,
160 gas_price: 1000,
161 },
162 ),
163 (
164 r#""0x1111111111111111111111111111111111111111: 2472666000 wei + 21000 gas × 1000 wei""#,
165 Summary {
166 to: Some(
167 "0x1111111111111111111111111111111111111111"
168 .parse::<Address>()
169 .unwrap(),
170 ),
171 value: U256::from(2472666000u64),
172 gas: 21000,
173 gas_price: 1000,
174 },
175 ),
176 ];
177 for (raw, typed) in valid_cases {
178 let deserialized = serde_json::from_str::<Summary>(raw).unwrap();
179 assert_eq!(deserialized, typed);
180
181 let serialized = serde_json::to_string(&typed).unwrap();
182 assert_eq!(serialized, raw);
183 }
184
185 let invalid_cases = [
186 r#"": ""#,
187 r#"" : 2472666000 wei + 21000 gas × 1000 wei""#,
188 r#""0x: 2472666000 wei + 21000 gas × 1000 wei""#,
189 r#""0x1111111111111111111111111111111111111111: 2472666000 wei""#,
190 r#""0x1111111111111111111111111111111111111111: 2472666000 wei + 21000 gas × ""#,
191 r#""0x1111111111111111111111111111111111111111: 2472666000 wei + 21000 gas × 1000""#,
192 ];
193 for raw in invalid_cases {
194 let summary: Result<Summary, _> = serde_json::from_str(raw);
195 assert!(summary.is_err());
196 }
197 }
198}